TypeScript泛型
什么是泛型
软件工程中,我们不仅要创建一致的定义良好的API,同时也要考虑可重用性。 组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型,这在创建大型系统时为你提供了十分灵活的功能。 使用
泛型
来创建可重用的组件,一个组件可以支持多种类型的数据。 这样用户就可以以自己的数据类型来使用组件。
平时我们是对 **值 **进行编程,泛型是对 类型 进行编程,用来限定值和对值的操作。
function foo(value: number): number {
return value
}
不可拓展! 使用联合类型或函数重载,过于麻烦。 如果换成any类型,就没有意义。失去了定义返回类型的能力,也失去了类型保护的能力。
function foo<T>(value: T): T {
return value
}
foo(1) // number
foo('1') // string
TypeScript是静态分析工具,不应该依赖js运行时。 这种可以适用于多种类型的函数此时叫做泛型。不同于any, 不会丢失信息,可以保持准确性。 特点: 函数名后添加了代表类型变量的**T。**它只代表类型,并不代表具体的值。 T可以用任何有效名称代替,常见的
- K(key)表示对象中的键类型
- V(value)表示对象中的值类型
- E(element)表示元素类型
可以同时添加多个类型变量
function foo<T, U>(value1: T, value2: U): T {
console.log(value1, value2)
return value1
}
泛型接口
interface Foo<T, U> {
value1: T,
value2: U
}
function foo<T, U> (value1: T, value2: U): Foo<T, U> {
console.log(value1 + ' is ' + typeof(value1))
console.log(value2 + ' is ' + typeof(value2))
let result:Foo<T, U> = {
value1,
value2
}
return result
}
// ==================
type FC<P = {}> = FunctionComponent<P>; // 定义泛型FC,起个别名
interface FunctionComponent<P = {}> {
(props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null; // 表明接受props和context返回ReactElement或null
propTypes?: WeakValidationMap<P>;
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
type PropsWithChildren<P> = P & { children?: ReactNode }; // 交叉类型 向props中插入children
泛型类
与函数泛型类似,在类名后面增加<T, ...>的语法定义。
class GenericPerson<T> {
name: T
getName: (name: T) => T
}
let person = new GenericPerson<number>()
person.name = 2
person.getName = function('xx') { return 'xx' } //Type '(: number) => string' is not assignable to type '(name: number) => number'.
使用泛型接口的类
interface ClassGenericInterface<T> {
value: T,
getData: () => T
}
class ClassGeneric<T> implements ClassGenericInterface<T> {
value: T,
constructor(value: T) {
this.value = value
}
getData(): T {
return this.value
}
}
构造签名
function factory<T>(type: T): T {
return new type() // This expression is not constructable.
}
function factory<T>(type: {new(): T}): T {
return new type() // ok
}
形如 { new (): T } 是一个构造签名, 此时指定传入的type是可被构造的,实例化后的类型是泛型T。
类的静态属性不适用泛型
泛型不适用于类的静态属性。类的静态属性和静态方法不能被实例化,只能通过调用类来使用。
泛型约束
限制每个类型变量接受的类型,这就是泛型约束的作用。
确保属性存在
希望类型变量对应的类型上存在某些属性
interface Length {
length: number;
}
function foo<T extends Length>(arg:T):T {
console.log(arg.length) // 存在
return arg
}
可以逗号分隔多种约束类型
interface Length {
length: number;
}
interface Type {
size: number;
}
function foo<T extends Length, Type>(arg: T):T {
console.log(arg.length, arg.size)
return arg
}
检查对象上的健是否存在
keyof操作符,用于获取某种类型的所有键,返回类型是联合类型
interface Person {
name: string,
age: number,
location: string,
}
type K1 = keyof Person // "name" | "age" | "location"
type K2 = keyof Person[] // "number" | "length" | "push" | "concat" ...
type K3 = keyof {[x:string]: Person } // string | number
利用keyof,限制输入的属性名必须包含在keyof返回的联合类型中
function getProps<T, K extends keyof T> (obj: T, key: K): T[K] {
return obj[key]
}
这样的方案保证了参数key一定是对象中所含有的键,不会发生运行时错误。是一种类型安全的解决方案。
enum Difficulty {
Easy,
Intermediate,
Hard
}
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
let tsInfo = {
name: "Typescript",
supersetOf: "Javascript",
difficulty: Difficulty.Intermediate
}
let difficulty: Difficulty =
getProperty(tsInfo, 'difficulty'); // OK
let supersetOf: string =
getProperty(tsInfo, 'superset_of'); // Error
参数默认类型
当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推断出类型时,这个默认类型就会起作用。
interface A<T = string>{
name: T;
}
const strA: A = { name: "SS" }
const numbB: A<number> = { name: 111 }
- 有默认类型的类型参数被认为是可选的
- 必选的类型参数不能在可选的类型参数后
- 如果类型参数有约束,类型参数的默认类型必须满足这个约束
- 当制定类型实参时,只需要指定必选类型参数的类型实参。未指定的类型参数会被解析为他们的默认类型
- 如果指定了默认类型,且类型推断无法选择一个候选类型,那么将使用默认类型作为推断结果
- 一个被现有类或接口合并的类或接口的声明可以为现有类型参数引入默认类型
- 一个被现有类或接口合并的类或接口的声明可以引入新的类型参数,只要它指定了默认类型
泛型条件类型
条件类型会以一个表达式进行类型关系检测,从而在两种类型中选择其一。使用infer关键字实现类型抽取。 infer表示extends中待推定的类型变量
interface Dictionary<T = any> {
[key: string]: T;
}
type StrDict = Dictionary<string>
type DictMember<T> = T extends Dictionary<infer V> ? V : never
type StrDictMember = DictMember<StrDict> // string
在 Promise对象中的使用
async function stringPromise() {
return "Hello, Semlinker!";
}
interface Person {
name: string;
age: number;
}
async function personPromise() {
return { name: "Semlinker", age: 30 } as Person; // 类型断言
}
type PromiseType<T> = (args: any[]) => Promise<T>;
type UnPromisify<T> = T extends PromiseType<infer U> ? U : never;
type extractStringPromise = UnPromisify<typeof stringPromise>; // string
type extractPersonPromise = UnPromisify<typeof personPromise>; // Person
泛型工具类型
Typescript内置了一些工具泛型,灵活使用可以使我们的Ts更加强大。 github.com/Microsoft/T…
Partial
作用是将某个类型里的属性全部变为可选项。
keyof 可以用来取得一个对象接口的所有 key 值; in 则可以遍历枚举类型
type Partial<T> = {
[P in keyof T]?: T[P];
};
Required
作用是将某个类型里的属性全部变为必选项。
-? 将可选项代表的?去掉
type Required<T> = {
[P in keyof T]-?: T[P];
};
Readonly
作用是将某个类型里的属性全部变为只读。
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
Record
作用是将某个类型里的属性都转为T类型
type Record<K extends keyof any, T> = {
[P in K]: T;
};
Pick
作用是将某个类型里的K取出来,并生成一个新的类型
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
Exclude/Extract
作用是将某个类型中属于另一个类型移除掉/提取出。
type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;
Omit
作用是用来忽略 T 中的 K 属性
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>> // 嵌套使用
NonNullable
去除 T 中包含的 null 或者 undefined
type NonNullable<T> = T extends null | undefined ? never : T;
Parameters
获取一个函数的参数类型,而且返回的是只能包含一组类型的数组
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
type Func = (user: string) => void
type Param = Parameters<Func>
let p: Param = ['1'] // 正确
p = ['1', '2'] // 错误,只能包含一个字符串类型值
ReturnType
得到一个函数的返回值类型
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
ConstructorParameters
获取一个类的构造函数参数类型,并以数组的形式返回
type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
InstanceType
获取一个类的实例类型
type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
泛型的类型推导
function id<T>(arg: T): T {
return arg;
}
id<string>("xiaodian");
id("xiaodian"); // 可以简写成这样
const [value, setValue] = useState<number>(1)
const [value2, setValue2] = useState(1)