在定义函数、接口或类不指定具体类型
无法创建泛型枚举和泛型命名空间
(1)函数泛型
function 名称<T=默认类型>(x:T,y:T):Array<T>{
...
}
名称<string>('str','str2'); 或者根据传入的参数自动地帮助我们确定T的类型
const fun:<T>()=>类型=<T>()=>{return 对应类型值}
(1.5)泛型默认值
T=类型
(2)泛型可定义多个
function 名称<T,C>(x:T,y:C):C{
...
}
名称<string,number>(1,'str');
名称(1,'str'); 合法,会进行类型推断
(3)泛型函数
有一个类型参数在最前面,像函数声明一样
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: <T>(arg: T) => T = identity;
let myIdentity: <U>(arg: U) => U = identity; 以使用不同的泛型参数名,只要在数量上和使用方式上能对应上就可以。
let myIdentity: {<T>(arg: T): T} = identity; 可以使用带有调用签名的对象字面量来定义泛型函数:
(4)泛型接口
interface GenericIdentityFn {
<T>(arg: T): T;
}
let myIdentity: GenericIdentityFn = identity;
把泛型参数当作整个接口的一个参数
interface GenericIdentityFn<T=类型> { 设置泛型默认类型
(arg: T): T;
}
let myIdentity: GenericIdentityFn<number> = identity; 传入一个<类型参数>来指定泛型类型
(5)类泛型(类的静态属性不能使用泛型类型。 )
示例一
class X:<T>{
constructor=(private xx:T){}
}
const x=new X<number>(1);
示例二
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
(6)在泛型里使用类类型
使用对象签名的形式
function create<T>(c: {new(): T; }): T { new()表示构造函数即传入一个类,T为返回值即实例,new ()的参数类型必须和传入的类的构造函数相同,如:new (...rest:any[ ])
return new c();
}
function createInstance<A extends Animal>(c: new () => A): A { 泛型继承类,表示传入的类只能是Animal的子类
return new c();
}
(7)继承限制泛型格式(使得参数必须包含接口中限制的属性)
1、继承接口(使得参数必须包含接口中限制的属性)
interface p{
name:string
例:
是length:number则可以传包含length属性的任何参数
[]数组
{length:2}
'字符串'
}
class P<T extends p>{
constructor(private data:T){}
}
const pp=new P({name:'123'}); 会进行类型推断
const pp=new P<{name:string,age:number}>({name:'123',age:12}); 可以扩充泛型属性
2、继承类型
class P<T extends number|string >{ 使得泛型只能是number或string类型,因为其他类型无这二者类型对应的属性或方法
constructor(private data:T){}
}
3、条件类型:添加extends关键字,根据条件切换不同类型
type t<T> =
T extends string ? 'string' :
T extends number ? 'number' :
"object";
(1)条件类型具有分布式调节特性:
t<string|string[]> 会返回联合类型string|object,即
(A | B) extends U ?X : Y
(A extends U?X ; Y | B extends U?X : Y)
例:
筛选条件类型(根据分布式调节特性)
type Diff<T,U>=T extends U ? never:T
如:
interface f{
a:'a',
b: 'b',
c:'c'
}
type a=Diff<keyof f,'a'|'e'> 返回类型为'b'|'c'
type a=Diff<'a'|'b'|'c','a'|'e'> 返回类型为'b'|'c'
筛选非null和undefined
type notNull<T>=Diff<T,undefined|null>
(2)条件类型中使用number类型指定索引对应值的类型
type a<T extends any[]> = {
name:T[number]
}
type c=a<[1,2,'ww']> 返回type c={1|2|'ww'}
(3)通过infer关键字引入一个待推断的类型变量
infer U类似展位符的操作,当满足条件时,U的类型即为传入的参数对应位置的类型
type Unpacked<T> =
T extends (infer U)[] ? U :
T extends (...args: any[]) => infer U ? U :
T extends Promise<infer U> ? U :
T;
type T0 = Unpacked<string>; string,满足最后一个分支,返回T即string
type T1 = Unpacked<string[]>; string,满足第一个条件,(infer U)[]对应string[],U对应string,则返回string
type T2 = Unpacked<() => string>; string,满足第二个条件,infer U代表返回值类型,故返回string
type T3 = Unpacked<Promise<string>>; string,返回第三个条件,infer U代表为string,则返回string
type T4 = Unpacked<Promise<string>[]>; Promise<string>,满足第一个条件,infer U代表为Promise<string>,故返回Promise<string>
type T5 = Unpacked<Unpacked<Promise<string>[]>>; string,同理
4、ReturnType类型
ReturnType<接收一个函数判断其返回值的类型>
例:
function f1(s: string) {
return { a: 1, b: s };
}
class C {
x = 0;
y = 0;
}
type T10 = ReturnType<() => string>; // string
type T11 = ReturnType<(s: string) => void>; // void
type T12 = ReturnType<(<T>() => T)>; // {}
type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>; // number[]
type T14 = ReturnType<typeof f1>; // { a: number, b: string },若只写f1表示值而非类型从而报错
type T15 = ReturnType<any>; // any
type T16 = ReturnType<never>; // any
type T17 = ReturnType<string>; // Error
type T18 = ReturnType<Function>; // Error
type T20 = InstanceType<typeof C>; // C
type T21 = InstanceType<any>; // any
type T22 = InstanceType<never>; // any
type T23 = InstanceType<string>; // Error
type T24 = InstanceType<Function>; // Error
(8)索引类型查询和索引访问
1、索引类型查询操作符
keyof
对于任何类型T,keyof T的结果为T上已知的公共属性名的联合.例如:
interface Person {
name: string;
age: number;
d:never 不能返回never类型对应的属性名
}
let personProps: keyof Person; 即 'name' | 'age'
2、索引访问操作符[ ],如实现对接口属性类型的访问,不能通过.的方式
使用方式一:
interface a{ name:string}
type b=a['name']; b为string
使用方式二:和[keyof T]结合
和[keyof]结合,剔除掉keyof之前运算结果中为never和keyof之后的对象集合中类型为never的属性
keyof前面只能是{}形式
type d<T> = {
[p in keyof T]: T[p] extends Function ? never:T[p]
}[keyof T]
interface P{
name: string,
age:never,
add:()=>{}
}
type h=d<P> 返回结果:type h = string
应用:
(1)能够遍历接口,返回键名
(2)使得传入的参数只能是接口定义的键名,返回值的类型只能是接口键名的类型
只有一个索引
interface xx{
name:string;
ages:number;
gender:string;
}
const info:xx...
function getInfo<T extends keyof xx>(key:T):xx[T]
{
T为xx上的键名
return this.info[key];
}
有两个索引
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
T:传入的集合
K:集合的键名,类型为:"键名"
T[K]:集合中K属性代表的类型
return names.map(n => o[n]);
}
例:
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
return names.map(n => o[n]);
}
interface Person {
name: string;
age: number;
}
let person: Person = {
name: 'Jarid',
age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, string[]
(9)索引类型和字符串索引签名
keyof和 T[K]与字符串索引签名进行交互。 如果你有一个带有字符串索引签名的类型,那么 keyof T会是 string。 并且 T[string]为索引签名的类型:
interface Map<T> {
[key: string]: T;
}
let keys: keyof Map<number>; // string
let value: Map<number>['foo']; // number
(10)映射类型(也是使用索引改造,区别是使用...in...),可以理解为一个构造函数
type Nullable<T> = { [P in 属性模板]: T[P] }
属性模板:'option1' | 'option2'或使用keyof返回的结果
P:绑定集合中的键
T[P]:键所对应的类型
例:
如将该接口改造成所有属性只读/可选
interface Person {
name: string;
age: number;
}
(1)创建映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
type Partial<T> = {
[P in keyof T]?: T[P];
}
(2)传入需改在的类型
type PersonPartial = Partial<Person>;
type ReadonlyPerson = Readonly<Person>;
type NullablePerson = { [P in keyof Person]: Person[P] | null }
type Nullable<T> = { [P in keyof T]: T[P] | null }
内置映射类型
T和U为接口类型/type
Readonly<T> --将T中的类型变成只读
Partial<T> --将T中的类型变成可选
Pick<T,'属性名'|'属性名'> --从T中抽取出指定的属性名
Record<'新属性名'|'新属性名',类型> --将给定的属性名全部赋予指定的类型,非同态类型,本质上会创建新的属性,因此它们不会从它处拷贝属性修饰符
Exclude<T, U> -- 从T中剔除可以赋值给U的类型。
Extract<T, U> -- 提取T中可以赋值给U的类型。
NonNullable<T> -- 从T中剔除null和undefined。
ReturnType<T> -- 获取函数返回值类型。
Omit<T,K>-- 从T中去掉K中的类型。
Omit<Todo, "completed" | "createdAt">;
InstanceType<T> -- 获取构造函数类型的实例类型。
class A{}
type q = InstanceType<typeof A>
let f: q = new A();
(11)+、-指定增加/删除,指定修饰符,不需要成对出现,+可省略
type Read<T> = { 增加修饰符
+readonly [p in keyof T]?:T[p]
}
type RemoveRead<T>={ 减去修饰符
-readonly [p in keyof T]-?:T[p]
}
type aa = {
name: string,
age:number
}
type read = Read<aa>;
type unread = RemoveRead<read>; 会去掉read中的readonly和?可选修饰符
(12)映射类型设置数组/元组类型,和索引签名效果相同
type arr2<T> = { 映射类型格式,可理解为转换成{0:类型,1:类型},鸭式结构使用数组
[p in keyof T]:T[p]
}
type arr = { 索引签名格式
[index: number]:string;
}
let arr: arr = ['2', '1'];
let arr2: arr2<[string,string]> = ['2', '1'];
(13)由映射类型推断原始类型(即通过写好的原始类型,逆向还原)
例:
分包:将一个对象改造成:{属性:{get(){return obj[x]},set(value){obj[x]=value}}}的形式
(1)定义对象的属性对应的类型别名
type proxy<T> = { T为属性类型
get(): T;
set(val: T): void;
}
(2)定义改造后对象的类型别名
type proxify<T> = {
[p in keyof T]:proxy<T[p]> T为对象,T[p]为每个属性的类型
}
(3)定义改造函数
function pro<T>(obj: T): proxify<T> {
let res = {} as proxify<T>;
for (const p in obj) { 改造传入的对象
res[p] = {
get() { return obj[p] },
set(val){obj[p]=val}
}
}
return res;
}
拆包:将分包改造的形式还原成原始形式{属性:值}
即根据分包的逻辑,逆向还原,即通过每个属性的get方法,获取到原始值,进而转换
function unPro<T>(obj: proxify<T>): T {
let res = {} as T;
for (const p in obj) {
res[p] = obj[p].get();
}
return res;
}
interface Person{
name:string;
ages:number;
gender:string;
}
class Teacher{
constructor(private info:Person){}
getInfo<T extends keyof Person>(key:T):Person[T]
{
return this.info[key];
}
}
const teacher=new Teacher({
name:'jeff2',
ages:16,
gender:'male'
})
const test=teacher.getInfo('name');
console.log(test);