为什么使用 ts
提供类型检查,减少错误
更友好的代码提示
增强代码可读性和可维护性
更好的代码重构
更好的代码组织和管理
JS 的超集,扩展新功能
转换 .ts 文件并不被浏览器所识别,运行不了。
所以需要转换为 .js 文件才能被浏览器所识别并运行。
我们可以安装 typescript,使用它提供的 tsc 来编译 .ts 文件。
安装:npm i -g typescript
转换:tsc xx.ts
不过,重新修改文件后需要重新运行转换命令。
如果想修改后自动进行装换,那么需要使用下面的命令。
自动监听的转换命令:tsc xx.ts -w
.ts 文件设置全局或局部环境 .ts 文件默认是全局环境的。也就是说,我们现在有两个文件,一个 a.ts,一个 b.ts。
a.ts 中定义了一个变量 a,let a = 1,在 b.ts 中输出变量 a,console.log(a)。
上面的代码不是不有警告的,b.ts 能够知道全局环境中有定义的 a 变量。
这样就不好了,我们希望 b.ts 只知道自己定义的 a 变量,而不应该知道全局环境中定义的 a 变量。
解决办法: 只要文件使用模块化操作,那么该文件就会变为局部环境。
方法: 在每个文件末尾添加 export {},这样 a.ts、b.ts 就变成了一个个独立的模块,b.ts 就只能使用自己定义的 a 变量了。
生成 tsconfig.json 文件 命令:tsc --init
联合类型 多个类型,只有符合其中的一个条件即可。
类型之间进行或操作。
let a : string | number = '1' a = 1 type A = { name : string }type B = { age : number }let person1 : A | B = { name : 'zhangsan' }
交叉类型 多个类型,必须符合所有条件。
类型之间进行与操作。
type A = { name : string }type B = { age : number }let person1 : A & B = { name : 'zhangsan' , age : 18 }
never 表示永远不会出现的值的类型。
很少用,一般是程序自动推断出来的。
let a : string & number = 1
也可以手动定义。
function foo (n: 1 | 2 | 3 ) { switch (n) { case 1 : break case 2 : break case 3 : break default : let m : never = n break } }
any 表示任意类型,任何类型都可以赋值给 any 类型。
any 使用时,TS 不进行检测。
unknown 表示 any 类型对应的安全类型。
unknow 使用时,TS 会进行检测。
类型断言 当 TS 推断出来的类型并不满足我们的使用需要时,可以使用类型断言来手动指定一个类型。
let a : unknown = [1 , 2 , 3 , 4 ];(a as []).map (() => { })
非空断言 数组 定义方法:
let arr1 : number [] = [1 , 2 , 3 , 4 ]let arr2 : string [] = ['1' , '2' , '3' , '4' ]let arr3 : Array <number > = [1 , 2 , 3 , 4 ]let arr4 : Array <string > = ['1' , '2' , '3' , '4' ]let arr5 : (string | number )[] = ['1' , '2' , 3 , 4 ]
元祖 表示一个已知元素数量和类型的数组,各元素的类型不必相同。
注意定义的元素类型和元素的数量和它的类型必须一致。
let arr1 : [string , number ] = ['1' , 1 ]let arr2 : [string , number , string ] = ['1' , 2 , '3' ]
定义对象、可选属性、索引签名 type Person = { name : string age : number , sex?: string [index : string ]: any } const person : Person = { name : 'John' , age : 30 , address : 'New York' }
需要注意的是,代码的提示只会提示定义的属性,不会提示索引签名的属性。
如上面会提示的属性有 name、age、address。
空数组、空对象 const arr : number [] = [] type ObjType = { name : string , age : number } const obj = {} as ObjType
定义函数、void类型 函数的实参个数和形参个数必须一致。
function foo1 (a : string , b?:number ): number { return 123 } foo1 ('1' , 2 )const foo2 = (a : string , b?: number ): number => { return 123 } foo2 ('1' , 2 )const foo3 : (a: string , b?: number ) => number = (a, b ) => { return 123 } foo3 ('1' , 2 )type FooType = (a: string , b?: number ) => number const foo4 : FooType = (a, b ) => { return 123 } foo4 ('1' , 2 )
void 类型是函数没有返回值的类型。
function foo1 () { }function foo2 (): void { return } function foo3 (): void { return undefined }
如果返回的是 undefined 需要手动指定返回类型为 undefined。
函数重载 现在需要实现一个函数,它接受一个、两个、四个参数,不能只传三个参数,因为第三个、第四个参数是搭配一起的。
function foo (n1: string ): any function foo (n1: string , n2: number ): any function foo (n1: string , n2: number , n3: string ,n4: string ): any function foo (n1: string , n2?: number , n3?: string , n4?: string ): any {}foo ('1' )foo ('1' , 2 )foo ('1' , 2 , '3' , '4' )
可调用注解 type Foo = { (n1 : string ): any (n1 : string , n2 : number ): any (n1 : string , n2 : number , n3 : string , n4 : string ): any username?: string } function foo (n1: string , n2?: number , n3?: string , n4?: string ): any {}let fn : Foo = foofn ('1' )fn ('1' , 2 )fn ('1' , 2 , '3' , '4' )fn.username = 'haha' console .log (fn.username )
枚举 可以通过枚举的属性名获取枚举的值,也可以通过枚举的值获取枚举的属性名。
enum UserType { SUPER_ADMIN , ADMIN , USER } console .log (UserType .SUPER_ADMIN ) console .log (UserType .ADMIN ) console .log (UserType .USER ) console .log (UserType [0 ]) console .log (UserType [1 ]) console .log (UserType [2 ])
可以手动定义枚举的值。后面的枚举值会自动递增。
enum UserType { SUPER_ADMIN , ADMIN = 3 , USER } console .log (UserType .SUPER_ADMIN ) console .log (UserType .ADMIN ) console .log (UserType .USER ) console .log (UserType [0 ]) console .log (UserType [3 ]) console .log (UserType [4 ])
也可以定义为字符串,但定义为字符串后,所有的枚举值都必须手动赋值。
enum UserType { SUPER_ADMIN = 'super_admin' , ADMIN = 'admin' , USER = 'user' } console .log (UserType .SUPER_ADMIN ) console .log (UserType .ADMIN ) console .log (UserType .USER )
enum UserType { SUPER_ADMIN = 'super_admin' , ADMIN = 2 , USER = 'user' } console .log (UserType .SUPER_ADMIN ) console .log (UserType .ADMIN ) console .log (UserType .USER )
const 枚举 未使用 const 定义的枚举,编译结果对比
enum UserType { SUPER_ADMIN = 'super_admin' , ADMIN = 2 , USER = 'user' } console .log (UserType .SUPER_ADMIN ) console .log (UserType .ADMIN ) console .log (UserType .USER )
编译结果
var UserType ;(function (UserType ) { UserType ["SUPER_ADMIN" ] = "super_admin" ; UserType [UserType ["ADMIN" ] = 2 ] = "ADMIN" ; UserType ["USER" ] = "user" ; })(UserType || (UserType = {})); console .log (UserType .SUPER_ADMIN ); console .log (UserType .ADMIN ); console .log (UserType .USER );
使用 const 定义的枚举,会在编译时被移除,不会生成对应的代码。
const enum UserType { SUPER_ADMIN = 'super_admin' , ADMIN = 2 , USER = 'user' } console .log (UserType .SUPER_ADMIN ) console .log (UserType .ADMIN ) console .log (UserType .USER )
编译后
console .log ("super_admin" ); console .log (2 ); console .log ("user" );
接口 与 类型别名 的区别 接口是一系列抽象方法的声明,是一些方法特征的集合。
简单来说,接口的作用就是为这些类型命名和为我们的代码或第三方代码定义契约。
区别:
接口主要用于对象类型,类型别名均可以。
相同接口定义可以进行合并,类型别名不行。
接口支持继承,类型别名不行。
类型别名支持映射类型,接口不支持。
type A = string type B = number []interface C { }interface D { a : string } interface D { b : string } const e : D = { a : 'a' , b : 'b' } interface F extends D { c : string }
字面量类型 可以把字面量作为具体的类型使用,需要注意的是,该类型的取值就必须是该字面量的值。
type Type = 'admin' | 'test' const userType : Type = 'admin'
keyof 关键字 interface IObj { name : string age : number } const user : keyof IObj = 'age' const obj = { name : 'lisi' , age : 18 } const user2 : keyof typeof obj = 'age'
类型保护 类型保护允许我们使用更小范围下的对象类型。
typeof
instanceof
in
字面量类型
function fn (n: number | string ) { if (typeof n === 'string' ) { console .log (n.length ) } } class userName { name!: string } class userAge { age!: number } function fn1 (n: userName | userAge ) { if (n instanceof userName) { console .log (n.name ) } } function fn2 (n: {name: string } | {age: number } ) { if ('name' in n) { console .log (n.name ) } } function fn3 (n: 'a' | 123 ) { if (n === 'a' ) { console .log (n.length ) } }
自定义类型保护 function isStr (n: any ): n is string { return typeof n === 'string' } function fn (n: number | string ) { if (isStr (n)) { console .log (n.length ) } } fn ('123' )
泛型 指在定义函数、接口或类时,未指定参数类型,在运行时才确定。
type A<T> = Tconst a : A<string > = 'string' const b : A<number > = 1 interface IA<T> { (n : T): T name?: T } const c : IA <string > = (n : string ): string => { return n}c.name = 'string' function fn<T> (n : T): T { return n } fn (1 )class AClass <T> { name : T constructor (name : T) { this .name = name } getName (): T { return this .name } setName (n : T): string { this .name = n return 'success' } } const d = new AClass <string >('string' )console .log (d.getName ())console .log (d.setName ('haha' ))
泛型约束 class AClass <T> { name : T constructor (name : T) { this .name = name } } const d = new AClass <string >('123' ) const e = new AClass <number >(123 )class BClass extends AClass <string > { } const f = new BClass ('string' )type B = string function fn1<T extends B> (n : T): T { return n } fn1 ('string' )
类型兼容性 类型兼容性用于确定一个类型是否能赋值给其他类型。
let a : string = '123' let b : string | number = 123 b = a interface A { a : number } interface B { a : number b : string } let c : A = { a : 1 }let d : B = { a : 1 , b : '1' }c = d function fn ({ a: number } ) {}fn ({ a : 1 })const value = { a : 1 , b : 2 }fn (value)
对于基础类型来说,少的可以赋值给多的。 对于对象类型来说,多的可以赋值给少的。 对于函数类型来说,实参多的可以赋值给形参少的。
映射类型 只能通过 类型别名 来定义映射类型。不能使用 接口 来定义映射类型。
type A = { username : string age : number } type B = { [p in keyof A]: A[p] } const a : B = { username : 'ss' , age : 12 }
keyof A 是获取类型 A 的所有属性名的联合类型,即 'username' | 'age'。p in keyof A 是遍历类型 A 的所有属性名,即 'username' | 'age'。A[p] 是获取 当前遍历属性 p 在 类型 A 中的类型,即 string | number。
内置工具类型 type A = { username : string age : number gender?: string , readonly address?: string } type a = Readonly <A> type b = Partial <A> type c = Pick <A, 'username' | 'age' > type d = Record <'age' , string > type e = Record <keyof A, string >type f = Required <A> type g = Exclude <string | number | boolean , boolean > type h = Extract <string | number | boolean , boolean > type i = Omit <A, 'username' | 'age' > type j = NonNullable <string | number | undefined | null >
infer 关键字 type A<T> = T extends Array <infer U> ? U : Ttype B = A<Array <number >>type C = A<string >
类如何使用类型 类中定义类型 class A { username!: string } class B { username : string = 'zhangsan' } class C { username : string constructor (username : string ) { this .username = username } } const a = new A ()a.username = 'zhangsan' const b = new B ()b.username = 'zhangsan' const c = new C ('zhangsan' )console .log (a, b, c); c.username = 'lisi' console .log (a, b, c);
类使用接口 interface IA { username : string getUserInfo (age : number ): string } class A implements IA { username : string = '张三' getUserInfo (age : number ): string { return `我叫${this .username} ,今年${age} 岁了` } } const a = new A ()console .log (a.getUserInfo (18 ))
类使用泛型 class A <T> { username : T constructor (username: T ) { this .username = username } } const a = new A<string >('123' )class B extends A <number >{ }const b = new B (123 )interface IA<T> { username : T getUserInfo (age : number ): string } class A implements IA <string > { username : string = '张三' getUserInfo (age : number ): string { return `我叫${this .username} ,今年${age} 岁了` } } const a = new A ()console .log (a.getUserInfo (18 ))