TS产生的原因 Js没有面向对象的设计理念,不适合开发大型项目。Ts是js的超集 ,包含了js的所有元素,同时还增加了静态类型、类、模块、接口和类型注解方面的功能。JS是一门弱类型语言,很多错误只有在运行时才会被发现。而TS提供了一套静态检测机制,可以在编译时就发现错误。
TS和JS的区别
语言层面:JS和TS都是ECMAScript的体现
执行环境层面:浏览器和node.js都能直接运行JS,但是不能直接运行TS
时序层面:TS被真正执行前,会通过编译转换生成JS,之后才能被解释执行
厂商层面:JS由Netscape率先推出,由各大浏览器厂商实现。TS由微软推出,由微软维护。
总结:TS是JS的语法糖,JS程序可以直接移植到TS,TS需要编译成JS才能被浏览器执行。
TS数据类型 基本数据类型 boolean, object, string, symbol, bigint, undefined, null, number
null、undefined 默认情况下,null和undefined是所有类型的子类型,也就是可以把null和undefined赋值给其他类型
引用数据类型–数组
1 2 let arr : string [] = ["1" , "2" ];let arr : Array <string > = ["1" , "2" ];
1 let arr : (number | string )[] = [1 , "2" ];
1 2 3 4 5 interface Arrobj { Name : string , Age : number } let arr : Arrobj [] = [{name : "hi" , age : 11 }];
引用数据类型—函数
1 function sum (x: number , y:number ): number {return x+y;}
1 let mySum : (x: number , y: number ) => number = function (x: number , y: number ): number {return x+y;}
1 2 3 interface searchFunc{ (source : string , substring : string ): boolean ; }
1 2 3 4 5 function push (array: any [], …items: any [] ){ items.forEach (function (item ) { array.push (item); }); } let a = [];push (a, 1 , 2 , 3 );
函数重载/方法重载:使用相同名称和不同参数数量或类型创建多个方法的能力
1 2 3 4 5 6 7 8 9 10 11 12 13 type Types = number | string ;function add (a: number , b: number ): number ;function add (a: string , b: number ): string ;function add (a: string , b: string ): string ;function add (a: string , b: string ): string ;function add (a: Types, b: Types ) { if (typeof a === "string" || typeof b === "string" ) { return a.toString () + b.toString (); } return a+b; } const result = add ("a" , "b" );result.split (" " );
引用数据类型—对象 小object代表的是所有非原始类型,所以不能把number\boolean\string\symbol\bigint等原始数据类型赋值给object。在严格模式下,null和undefined类型也不能赋值给object。
大Object代表所有拥有toString、hasOwnProperty方法的类型,所以所有原始类型、非原始类型都可以赋给Object。同样,在严格模式下,null和undefined类型也不能赋给Object。
{}与大Object一样。
元组 元组的特性是可以限制数组元素的个数和类型,它特别适合用来实现多值返回。适合用于保存定长定数据类型的数据。
1 let x : [string , number ];
元组解构 1 2 let employee : [number , string ] = [1 , "semlinker" ]; let [id, username] = employee;
其他数据类型–never never表示的是那些永不存在的值的类型。值永不存在的两种情况:1.如果函数执行时抛异常,那么这个函数永远不存在返回值。2.函数中执行无限循环的代码,使得程序永远无法运行到函数返回值。
never可以赋值给任何类型,但是没有类型能赋值给never类型(除本身),即使any也不可以
在ts中,可以利用never类型的特性来实现全面性检查:
1 2 3 4 5 6 7 8 9 10 11 type Foo = string | number ;function controlFlowAnalysisWithNever (foo: Foo ) { if (typeof foo === “string ”) { }else if (typeof foo === “number ”){ }else { const check : never = foo; } }
any 变量在声明时未指定类型,则会被识别为any类型
unknown unknown与any类似。它们的最大区别是:任何类型的值可以赋值给any,同时any类型的值也可以赋值给任何类型。unknown任何类型都可以赋值给它,但它只能赋值给unknown和any.
类型断言语法 1 2 3 4 5 6 7 8 9 let someValue : any = “this is a string ”;let strLength : number = (<string >someValue).length ;let someValue : any = “this is a string ”;let strLength : number = (someValue as string ).length ;
后缀表达式操作符!可以用于断言操作对象是非null和非undefined类型,例如:x!将从x值域中排除null和undefined.
1 2 3 let mayNullOrUndefinedOrString : null | undefined | string ;mayNullOrUndefinedOrString!.toString (); mayNullOrUndefinedOrString.toString ();
允许在实例属性和变量声明后面放置!号,从而告诉ts该属性会被明确地赋值.
1 2 3 4 5 6 let x : number ;initialize ()console .log (2 * x); function initialize ( ){ x = 10 ; }
1 2 3 4 5 6 let x! : number ;initialize ()console .log (2 * x); function initialize ( ){ x = 10 ; }
交叉类型 交叉类型是将多个类型合并为一个类型。这让我们可以把现有的多种类型叠加到一起成为一种类型,它包含所需的所有类型的特性,使用&定义交叉类型。
如果仅仅把原始类型、字面量类型、函数类型等原子类型合并成交叉类型,是没有用处的,因为没有类型能同时属于多种原子类型,比如既是string类型又是number类型。交叉类型可以将多个接口类型合并成一个类型 ,从而实现等同接口继承的效果。
1 2 3 4 5 6 type IntersectionType = {id : number ; name : string ;} & {age : number };const mixed : IntersectionType = { id : 1 , name : ‘name’, age : 18 }
任意属性 除包含必选和可选属性之外,还允许有其他的任意属性,可以使用索引签名的形式来满足上述要求。一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集。
1 2 3 4 5 6 7 8 9 interface Person { name : string ; age?: number ; [propName : string ]: any ; } let tom : Person = { name : ‘Tom ’, gender : ‘male’ };
鸭式辨型法:通过某个规则来判定对象是否实现了这个接口 1 2 3 4 5 6 7 8 interface LabeledValue { label : string ; } function printLabel (labeledValue: LabeledValue ) { console .log (labeledValue.label ); } let myObj = {size : 10 , label : "size 10 object" };printLabel (myObj);
1 2 3 4 5 6 7 interface LabeledValue { label : string ; } function printLabel (labeledValue: LabeledValue ) { console .log (labeledValue.label ); } printLabel ({size : 10 , label : "size 10 object" });
在参数里写对象就相当于直接给labeledObj赋值,这个对象有严格的类型定义,不能多参和少参。而当你在外面使用另一个变量myObj接收,myObj不会经过额外属性检查,但会根据类型推论为let myObj: {size: number; label: string} = {size: 10, label: “size 10 Object”};,然后将myObj再赋值给labeledObj,此时根据类型的兼容性,两种类型对象,参照鸭式辨型法,因为都具有label属性,所以被认定为两个相同,故可以绕开多余的类型检查。
绕开额外属性检查的方式
1 2 3 4 interface Props { name : string ; [key : string ]: any ; }
ts中接口与类型的区别 大多数情况下使用接口类型和类型别名的效果等价。但是两者也存在很多不同。Ts的核心原则之一是对值所具有的结构进行类型检查。而接口的作用就是为这些类型命名和为代码或第三方代码定义数据模型。Type(类型别名)会给类型起个新名字,但是它不会创建新类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 interface Point { x : number ; y : number ; } interface setpoint{ (x : number , y : number ): void ; } type Point = { x : number ; y : number ; } type setpoint = (x: number , y: number ) => void ;
与接口不同,类型别名可以用于其他类型,如基本类型、联合类型、元组
1 2 3 4 5 6 7 8 9 10 11 12 type Name = string ;type PartialPointX = {x : number };type PartialPointY = {y : number };type PartialUnion = PartialPointX | PartialPointY ;type Data = [number , string ];let div = document .createElement (‘div’);type B = typeof div;
1 2 3 interface Point {x : number ;}interface Point {y : number ;}const point : Point = {x : 1 , y : 2 };
两者的扩展方式不同,但并不互斥。接口可以扩展类型别名,类型别名也可以扩展接口。接口的扩展是通过继承,通过extends
来实现。类型别名的扩展就是交叉类型,通过&
来实现。
接口扩展接口
1 2 3 4 5 6 interface PointX { x : number ; } interface Point extends PointX { y : number ; }
类型别名扩展类型别名
1 2 3 4 5 6 type PointX = { x : number ; } type Point = PointX & { y : number ; }
接口扩展类型别名
1 2 3 4 5 6 type PointX = { x : number ; } interface Point extends PointX { y : number ; }
类型别名扩展接口
1 2 3 4 5 6 interface PointX { x : number ; } type Point = PointX & { y : number ; }
泛型 1 2 3 4 5 function identity<T, U>(value : T, message : U): T { console .log (message); return value; } console .log (identity<Number , string >(68 , "ssss" );
泛型约束 如果想使用泛型上的属性,例如size,如果直接使用会报错。直观的想法是限定函数的参数类型应该有某属性,使用extends关键字可以做到这点。
1 2 3 4 5 6 7 interface Sizeable { size : number ; } function trace<T extends Sizeable > (arg : T): T{ console .log (arg.size ); }
泛型工具类型
typeof:在类型上下文中获取变量或者属性的类型
1 2 3 4 5 6 interface Person { name : string ; age : number ; } const sem : Person = {name : “sss”, age : 30 };type Sem = typeof sem;
keyof:用于获取某种类型的所有键,其返回类型是联合类型
1 2 3 4 5 6 7 interface Person { name : string ; age : number ; } type k1 = keyof Person ; type k2 = keyof Person []; type k3 = keyof {[x : string ]: Person };
Ts中支持字符串索引和数字索引:为同时支持两种索引类型,就要求数字索引的返回值必须是字符串索引返回值的子类。因为当使用数字索引时,JS在执行索引操作时,会先把数值索引转换为字符串索引。
1 2 3 4 5 6 7 8 interface StringArr { [index : string ]: string ; } interface StringArr1 { [index : number ]: string ; } type k1 = keyof StringArr ; type k2 = keyof StringArr1 ;
keyof也支持基础数据类型
1 2 3 let k1 : keyof boolean ; let k2 : keyof number ; let k3 : keyof symbol ;
经常遇到这个报错:Element implicitly has an ‘any’ type because expression of type ‘string’ can’t be used to index type ‘{}’ . 这是因为元素隐式拥有any类型,string类型不能被用于索引{}类型。
解决方案有两个:一是暴力,这样失去了类型限制的意义。二是继承。
1 2 3 4 5 6 7 8 9 function prop (obj: object , key: string ) { return (obj as any )[key]; } function prop<T extends object , K extends keyof T>(obj : T, key : K) { return obj[key]; }
1 2 3 4 type keys = “a” | “b” | “c”;type Obj = { [p in keys]: any }
infer:可以用于声明一个类型变量并对它进行使用。infer可以通过已知的类型和获得它泛型反推出泛型参数
1 2 3 4 type ReturnType <T> = T extends ( ...args : any [] ) => infer R ? R : any ;
1 2 type getIntersection<T> = T extends (a : infer P, b : infer P) => void ? P : never ;type Intersection = getIntersection<(a : string , b : number )> => void ;
1 2 3 4 5 6 7 8 interface Lengthwise { length : number ; } function loggingIdentity<T extends Lengthwise >(arg : T): T { console .log (arg.length ); return arg; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 interface TestInterface { name :string , age :number } type OptionalTestInterface <T> = { [p in keyof T]+?:T[p] } type newTestInterface = OptionalTestInterface <TestInterface >
内置工具类型—Partial:将类型的属性变成可选。但是Partial有个局限性,就是只支持处理第一层的属性,如果想处理多层属性需要使用DeepPartial
1 2 3 type Partial <T> = { [P in keyof T]?: T[P]; };
1 2 3 4 5 6 type DeepPartial <T> = { [U in keyof T]?: T[U] extends object ? DeepPartial <T[U]> : T[U] }; type PartialedWindow = DeepPartial <T>;
1 2 3 4 type Required <T> = { [P in keyof T]-?: T[P] };
Readonly:将某个类型所有属性变为只读属性,也就意味着这些属性不能被重新赋值
Pick:从某个类型中挑出一些属性来
1 2 3 4 5 6 7 8 9 10 11 12 13 type Pick <T, K extends keyof T> = { [P in K]: T[P]; } interface Todo { title : string ; description : string ; completed : boolean ; } type TodoPreview = Pick <Todo , "title" | "completed" >;const todo : TodoPreview = { title : 'clean room' , completed : false }
Record<K extends keyof any, T>:将K中所有的属性的值转为T类型
1 2 3 4 5 6 7 8 9 10 11 12 typeof Record <K extends keyof any , T> = { [P in K]: T; } interface PageInfo { title : string ; } type Page = "home" | "about" | "contact" ;const x : Record <Page , PageInfo > = { about : {title : "about" }, contact : {title : "contact" }, home : {title : "home" }, }
ReturnType :用来得到一个函数的返回值类型
1 2 3 4 5 6 7 8 9 type ReturnType <T extends (...args : any []) => any > = T extends ( ...args : any [] ) => infer R ? R : any ; type Func = (value: number ) => string ;const foo : ReturnType <Func > = "1" ;
Exclude<T, U>:将某个类型中属于另一个的类型移除掉
1 2 3 4 5 6 type Exclude <T, U> = T extends U ? never : T;type T0 = Exclude <"a" | "b" | "c" , "a" >; type T1 = Exclude <"a" | "b" | "c" , "a" | "b" >; type T2 = Exclude <string | number | (() => void ), Function >;
1 2 3 4 5 type Extract <T, U> = T extends U ? T : never ;type T0 = Extract <"a" | "b" | "c" , "a" | "f" >; type T1 = Extract <string | number | (() => void ), Function >;
Omit<T, K extends keyof any>的作用是使用T类型中除了K类型的所有属性,来构造一个新的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 type Omit <T, K extends keyof any > = Pick <T, Exclude <keyof T, K>>;interface Todo { title : string ; description : string ; completed : boolean ; } type TodoPreview = Omit <Todo , "description" >;const todo : TodoPreview = { title : "Clean room" , completed : false , };
NonNullable:用来过滤类型中的null及undefined
1 2 3 4 5 6 type NonNullable <T> = T extends null | undefined ? never : T;type T0 = NonNullable <string | number | undefined >; type T1 = NonNullable <string [] | null | undefined >;
Parameters:用于获得函数的参数类型组成的元组类型
1 2 3 4 5 6 7 8 9 type Parameters <T extends (...args : any ) => any > = T extends (...args : infer P) => any ? P : never ; type A = Parameters <() => void >; type B = Parameters <typeofArray.isArray >; type C = Parameters <typeofparseInt>; type D = Parameters <typeofMath.max >;
tsconfig.json 作用 ts项目的配置文件,包含ts编译的相关配置,通过更改编译配置项,可以让ts编译出ES6、ES5、node的代码。
文件中重要字段
files: 设置要编译的文件的名称
include: 设置需要进行编译的文件,支持路径模式匹配
exclude:设置无需进行编译的文件,支持路径模式匹配
compilerOptions:设置与编译流程相关的选项