typescript 泛型、索引类型查询和索引访问、映射类型、条件类型、映射类型推断

在定义函数、接口或类不指定具体类型

无法创建泛型枚举和泛型命名空间

(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;

}

/**
 * type T ='name'
 * key:'name'
 * Person['name']; 即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);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值