前景概要
在上一篇声明文章里,变量、参数、函数返回值的类型都是在已知的前提下声明的,在真是情景中,也会有我们定义的时候未知其类型的变量或参数,在考虑复用性的情况下泛型给我们提供了强大而灵活的功能,可以支持多种类型的数据。
例子
我们想写一个函数,它拥有一个参数我们不知道它的类型但是我们希望函数的返回值的类型跟参数的类型一样。
- 方案1: 定义参数跟返回值的类型为 any,但是一旦我们这么做我们就是向类型检查做妥协,放弃了类型检查,并且不一定能够返回与参数同类型的值。
- 方案2:
方案1: any
function getValue(value: any):any {
... // 过程如果操作不当 valueTypeRes 的类型不是参数value的类型也会编译通过,返回值类型的正确性存在无法确认的风险。
return valueTypeRes;
}
方案2: 泛型
/**
* T 代表泛型,用其他大写字母也可以,建议用T,它的类型是调用方法的时候决定的
* 在使用泛型的方法里也可以用泛型定义变量的类型
*/
function getValue<T>(value:T):T {
return value;
}
// 调用的方式
getValue<string>('1');
// 调用的时候决定参数的类型为string,返回值也必须为string
泛型类
// 法语:class className:<T>{}
class exampleClass<T> {
public value:T;
exampleFunc(value:T):T {
return value;
}
}
泛型接口
泛型函数类型接口的语法跟函数类型接口一样,忘记了可以在声明文章里面查阅(传送门:TS属性、方法、类、接口声明大杂烩)
)
写法1
interface IExample {
<T>(value:T):T;
}
let getValue:IExample = <T>(value:T):T => value;
写法2
interface IExample<T> {
(value:T):T;
}
let getValue:IExample<number> = (value:number):number => value;
写法3
interface IExample<T> {
(value:T):T;
}
function getValue<T>(value:T):T {
return value;
}
let getNumberValue:IExample<number> = getValue;
模块
模块拥有其自身的作用域,定义在模块里的变量,方法,类等在模块外部是不可见的。不过我们可以使用 export 的方式导出它们。相反我们需要用到模块导出的变量、方法、类等的时候使用 import 引入它们,语法是es6的语法。
模块用例:平时的公共常量和公共的方法的抽离,就是抽离成一个模块。在使用的时候 import 它们。
命名空间
命名空间的概念跟模块的概念差不多,相当于是模块中的模块,它的存在有效避免多人同时开发时的命名冲突。
// 命名空间
/**
* 语法:namespace 空间名字 {}
* 在命名空间外部调取命名空间的变量、方法等:空间名字.变量 || 方法等
*/
namespace spaceA {
interface IExmple {
valueA: string;
funcA(): string;
}
export class classA implements IExmple {
valueA: string;
constructor(value: string) {
this.valueA = value;
}
funcA() {
return 'success';
}
};
}
// 在外部调用命名空间属性
let objA = new spaceA.classA('value');
// 在命名空间里调用其他命名空间的属性
namespace spaceB {
let objB = new spaceA.classA('value');
// 接口命名和 spaceB 里一样不会冲突
interface IExmple {
valueA: string;
funcA(): string;
};
}
推荐:如果想要使得代码更简洁,也可以把每个命名空间单独抽离成一个模块~