typescript中interface和type学习

interface

interface 表示的是接口类型,相当于定义一个对象有哪些属性;
?:表示可选属性;
[k: ]: any;表示其他未知属性;

	interface Person {
	  name: string;
	  age:number;
	  fn?: (...args: any[]) => void;
	  [k: string]: any;
	}
	const person: Person = {
	  name: '张三',
	  age: 10,
	}

接口可以extends继承自另一个interface 接口 或者是接口类型的type

	interface Name {
	  name: string;
	}
	
	interface Person extends Name {
	  age: number;
	}

接口可以多次重复定义,最终合并效果同上

	interface Person {
	  name: string;
	}
	
	interface Person {
	  age: number;
	}

下面这种写法为什么会报错?

	interface Props {
	  [key: string]: string;
	}
	
	interface dataType {
	  title: string;
	}
	
	const data: dataType = {
	  title: '',
	};
	
	const props: Props = data;

	//不能将类型“dataType”分配给类型“Props”。
	//  类型“dataType”中缺少类型“string”的索引签名。

看似dataTypeProps的子集,实际dataType可以多次定义

	interface Props {
	  [key: string]: string;
	}
	
	interface dataType {
	  name: string;
	}
	
	const data: dataType = {
	  name: 'zzz',
	};
	
	const props: Props = data;
	
	interface dataType {
	  age: number;
	}

这样一看就知道是不能把dataType赋值给 Props的,这种时候就要用type去定义。

type

type 定义一个类型,可以是接口类型,也可以是基本类型包括:numberstringnullunderfinedboolean
或者是模板类型'hello'`hello ${sting}`等等;

	type Person = {
	  name: string;
	  age:number;
	  fn?: (...args: any[]) => void;
	  [k: string]: any;
	}
	type N = number;
	type S = 'a' | 'b';
	type P = Person;
	type T = `hello ${N}`;
...

泛型

泛型表示通过传入的类型确定最终的类型,用于支持多个类型数据;

	function identity<T>(arg: T): T {
	    return arg;
	}

当你不想通过any丢失信息,同时又具体用到了某些属性时,
可以通过extends泛型约束

	function arean<T extends { width: number; height: number }>(arg: T): number {
	  return arg.width * arg.height;
	}

交叉类型 &

T & R 表示 既是 T 又是 R

	interface Name {
	  name: string;
	}
	
	interface Age {
	  age: number;
	}
	
	type Person = Name & Age;

	
	const personOne: Person = {
	  name: '张三',
	  age: 22,
	}

&的作用是合并两个类型,一般情况下与接口extends作用差不多,
但是&不会做定义类型时的前置检查,例如:

	type A = {
	  a: string;
	  c: number;
	};
	
	type B = {
	  b: string;
	  c: string;
	};
	
	type C = A & B;
	
	//不能将类型“string”分配给类型“never”。
	const c1: C = {
	  a: '',
	  b: '',
	  c: '',
	};
	//不能将类型“number”分配给类型“never”。
	const c2: C = {
	  a: '',
	  b: '',
	  c: 1,
	};

上述例子应AB类型合并时c属性是即是number又是string,最终的结果:

	type C = {
      a: string;
	  b: string;
	  c: never;
	};

也有可能结果为never;取决于never的位置;
这种object类型的&生成的结果更像是并集;但是并不能把&看成并集

	type A = 'a'|'c'|'d';
	
	type B = 'b'|'c'|'d';
	
	type C = A & B; // ’c'|'d'

上面这个例子就是一个交集的效果;

无论赋什么值都会报错。
而当我们用extends时,会在定义时直接报错提醒:

	type A = {
	  a: string;
	  c: number;
	};
	
	//不能将类型“string”分配给类型“number”。
	interface C extends A {
	  b: string;
	  c: string;
	}

如果一个值是交叉类型,(有可能获取的是交集也可能是并集)。

联合类型丨

联合类型表示一个值可以是几种类型之一。 我们用竖线(|)分隔每个类型,所以 number | string | boolean表示一个值可以是 numberstring,或 boolean

如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。

官方提供的常用类型工具:

	// 使所有T的属性可选
	type Partial<T> = {
	    [P in keyof T]?: T[P];
	};
	
	// 使所有T的属性必选
	type Required<T> = {
	    [P in keyof T]-?: T[P];
	};
	
	// 使所有T的属性只读
	type Readonly<T> = {
	    readonly [P in keyof T]: T[P];
	};
	
	 // 从T中挑选在联合类型K的中属性
	type Pick<T, K extends keyof T> = {
	    [P in K]: T[P];
	};
	
	 // 构造一个key在K中,值为T的类型
	type Record<K extends keyof any, T> = {
	    [P in K]: T;
	};
	
	 // 从T类型中排除U类型
	type Exclude<T, U> = T extends U ? never : T;
	
	 // 从T类型中提取U共有类型
	type Extract<T, U> = T extends U ? T : never;
	
	// 从T中忽略K中的key
	type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
	
	// 从T中忽略null和underfined
	type NonNullable<T> = T & {};
	
	// 推断一个函数参数的类型
	type Parameters<T extends (...args: any) => any> = 
	T extends (...args: infer P) => any ? P : never;
	
	// 推断一个构造函数参数的类型
	type ConstructorParameters<T extends abstract new (...args: any) => any> = 
	T extends abstract new (...args: infer P) => any ? P : never;
	
	// 推断一个函数返回值的类型
	type ReturnType<T extends (...args: any) => any> = 
	T extends (...args: any) => infer R ? R : any;

类型推断

当使用联合类型或其他时,我们时常会遇到这种情况:

	interface Rect {
	  width: number;
	  height: number;
	  name: string;
	}
	interface Circular {
	  radius: number;
	  name: string;
	}
	
	function arean(arg: Rect | Circular) {
	  if (arg.name === 'Rect') {
	    return (arg as Rect).width * (arg as Rect).height;
	  }
	  if (arg.name === 'Circular') {
	    return Math.pow((arg as Circular).radius, 2) * Math.PI;
	  }
	  return 0;
	}
	
	arean({ name: 'Rect', width: 10, height: 10 });
	arean({ name: 'Circular', radius: 10 });

单纯就这个例子可以改进:

	interface Rect {
	  width: number;
	  height: number;
	  name: 'Rect';
	}
	interface Circular {
	  radius: number;
	  name: 'Circular';
	}
	
	function arean(arg: Rect | Circular) {
	  if (arg.name === 'Rect') {
	    return arg.width * arg.height;
	  }
	  if (arg.name === 'Circular') {
	    return Math.pow(arg.radius, 2) * Math.PI;
	  }
	  return 0;
	}
	
	arean({ name: 'Rect', width: 10, height: 10 });
	arean({ name: 'Circular', radius: 10 });

typescript会自动做类型推断,有时候我们不需要去自己写类型:

	let a = 1; // a:number
	a = ''; // 不能将类型“string”分配给类型“number”
	
	const B = 1; //  B: 1
	
	const C = {
	  a: 1,
	  b: 'b',
	  c: true,
	}; // C: {  a: number;  b: string;  c: boolean;}
	C.b = 2; // 不能将类型“number”分配给类型“string”。
	const A = {
	  a: 'a',
	  b: 'b',
	  /** 这是c属性 */
	  c: 'c',
	};
	A.c = 'aa'; // 提示:这是c属性
	A.d = ''; //类型“{ a: string; b: string; c: string; }”上不存在属性“d”。
	
	const B: { [key: string]: string } = {
	  a: 'a',
	  b: 'b',
	  c: 'c',
	};
	
	B.d = '';

上面例子A推导出的类型是一个映射,vscode会有属性提示;而B是一个{ [key: string]: string }是没有固定属性的 所以没有提示 。

通过/** 注释 */ 注释的,在引用时vscode会显示注释

官方推荐用interface,其他无法满足需求的情况下用type。实际应用中,定义接口用interface,通过现有interface的衍生使用type

补充Type

	// T类型在联合类型Keys中必选其一
	export type RequireAtLeastOne<T, Keys extends keyof T = keyof T> = Pick<
	  T,
	  Exclude<keyof T, Keys>
	> &
	  {
	    [K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>;
	  }[Keys];
	  
	interface A {
	  a: string;
	  b: boolean;
	  c: number;
	}
	
	const a: RequireAtLeastOne<A> = {
	  a: '',
	};
	
	const b: RequireAtLeastOne<A, 'a' | 'b'> = {
	  a: '',
	  c: 1,
	};

	// 获取T和R中的共有属性 
	export type ExtractCommon<T extends object, R extends object> = {
	  [K in NotNeverCommonKey<T, R>]: T[K];
	};
	
	type NotNeverCommonKey<T, R> = {
	  [K in keyof T & keyof R]: T[K] & R[K] extends never ? never : K;
	}[keyof T & keyof R];
	
	// test
	type A = {
	  a: string;
	  c: number;
	  d: string;
	};
	
	type B = {
	  b: boolean;
	  c: string;
	  d: string;
	};
	
	type C = ExtractCommon<A, B>; // { d: string; }

ps: 不当之处,欢迎指正。

你可能感兴趣的:(nodejs,typescript,学习,javascript)