类型 基础类型 null, undefined, symbol, boolean, void
1 2 const count: number = 123 const teacherName: string = 'Dell'
对象类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Person {}const teacher: { name: string ; age: number ; } = { name: "Dell" , age: 18 , }; const numbers: number [] = [1 , 2 , 3 ];const dell: Person = new Person();const getTotal: () => number = () => { return 123 ; };
type annotation and inference type annotation 类型注解, 我们告诉 TS 变量是什么类型
type inference 类型推断, TS 会自动地去尝试分析变量的类型
如果 TS 能够自动分析变量类型,就什么也不用做
1 2 3 4 5 let count: number ;count = 123 ; let countInference = 123 ;
如果 TS 无法分析变量类型的话,就需要使用类型注解
```typescript const firstNumber = 1; const secondNumber = 2; const total = firstNumber + secondNumber;// TS 推断出 total 是 number
1 2 3 4 5 6 7 ```typescript function getTotal(firstNumber: number, secondNumber: number) {// firstNumber 需要注解是number类型 return firstNumber + secondNumber; } const total = getTotal(1, 2) // total 推断出是number
函数相关类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function add (first: number , second: number ): number { return first + second; } function sayHello ( ): void { console .log("hellp" ); } function errorEmitter ( ): never { while (true ) {} } function add ({ first, second }: { first: number ; second: number } ): number { return first + second; }
基础语法复习
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 let count: number ;count = 123 ; const func = (str: string ) => { return parseInt (str, 10 ); }; const func1 = (str: string ) => (number = (str ) => { return parseInt (str, 10 ); }); const date = new Date (); interface Person { name: string ; } const rawData = { name : "dell" };const newData: Person = JSON .parse(rawData); let temp: number | string = 123 ;temp = "456" ;
数组和元组 数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 const arr: (number | string )[] = [1 , "2" , 3 ];const stringArr: string [] = ["a" , "b" , "c" ];const undefinedArr: undefined [] = [undefined ];type User = { name : string ; age: number };const objectArr: User[] = [ { name: "dell" , age: 13 , }, ]; class Teacher { name: string ; age: number ; } const objectArr: Teacher[] = [ new Teacher(), { name: "dell" , age: 28 , }, ];
元组 tuple
规定了数组的类型的顺序
1 2 3 4 5 6 const teacherInfo: [string , string , number ] = ["Dell" , "male" , 18 ];const teacherList: [string , string , number ][] = [ ["dell" , "male" , 18 ], ["sun" , "female" , 26 ], ];
interface 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 interface Person { readonly name: string ; age?: number ; } const getPersonName = (person: Person): void => { console .log(person.name); }; const serPersonName = (person: Person, name : string ): void => { person.name = name; }; const person = { name: "dell" , }; getPersonName(person); setPersonName(person, "lee" );
当实参直接传递字面量,会进行强校验 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 getPersonName({ name: "dell" , sex: "male" , }); const person = { name: "dell" , sex: "male" , }; getPersonName(person); interface Person { name: string ; age?: number ; [propName: string ]: any ; }
interface 方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 interface Person { name: string ; age?: number ; [propName: string ]: any ; say(): string ; } const person = { name: "dell" , sex: "male" , say ( ) { return "hello" ; }, }; getPersonName(person);
Class 规范于 interface 1 2 3 4 5 6 class User implements Person { name = "dell" ; say ( ) { return "hello" ; } }
interface 继承 1 2 3 interface Teacher extends Person { teach(): string ; }
interface 方法 1 2 3 4 5 6 7 interface SayHi { (word: string ): string ; } const say: SayHi = (word: string ) => { return word; };
Class 类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Person { name = "dell" ; getName ( ) { return this .name; } } class Teacher extends Person { getTeacherName ( ) { return "Teacher" ; } getName ( ) { return super .getName() + "lee" ; } } const teacher = new Teacher();console .log(teacher.getName());
public 允许在类的内外被调用
private 允许在类的内部被调用
protected 允许在类内及继承的子类中使用
constructor 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Person { public name: string ; constructor (name: string ) { this .name = name; } } class Person { constructor (public name: string ) {} } const person = new Person("dell" ); console .log(person.name);
super
如果想在子类中使用构造函数,需要调用super()
,手动调用父类的构造函数
1 2 3 4 5 6 7 8 9 10 11 12 class Person { constructor (public name: string ) {} } class Teacher extends Person { constructor (public age: number ) { super ("Shawn" ); } } const teacher = new Teahcer(24 );console .log(teacher.age, teacher.name);
setter 和 getter 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Person { constructor (private _name: string ) {} get name () { return this ._name + " Ng" ; } set name (name: string ) { const realName = name.split(" " )[0 ]; this ._name = realName; } } const person = new Person("dell" );console .log(person.name);person.name = "dell lee" ;
单例模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Demo { private static instance: Demo; private constructor ( ) {} static getInstance ( ) { if (!this .instance) { this .instance = new Demo(); } return this .instance; } } const demo1 = Demo.getInstance(); const demo2 = Demo.getInstance();
readonly 1 2 3 4 5 6 7 8 9 class Person { public readonly name: string ; constructor (name: string ) { this .name = name; } } const person = new Person("Dell" ); person.name = "hello" ;
抽象类 把几何属性抽象出来
抽象类的抽象方法和属性,继承类必须有
调用继承类的时候只能使用包含在抽象类中方法定义的和属性
抽象类不能直接实例化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 abstract class Geom { width: number ; getType ( ) { return "Geom" ; } abstract getArea(): number ; } new Geom(); class Circle extends Geom { getArea ( ) { return 12 ; } } class Square {}class Trangle {}
同理,抽象 Object 可以用 interface
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 interface Person { name: string ; } interface Teacher extends Person { teachingAge: number ; } interface Student extends Person { age: number ; } const teacher = { name: "dell" , teachingAge: 3 , }; const student = { name: "lee" , teachingAge: 13 , }; const getUserInfo = (user: Person ) => { console .log(user.name); }; getUserInfo(teacher); getUserInfo(student);
附一个官网例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 abstract class Department { abstract width: number ; constructor (public name: string ) {} printName(): void { console .log("Department name: " + this .name); } abstract printMeeting(): void ; } class AccountingDepartment extends Department { width = 123 ; height = 123 ; constructor ( ) { super ("Accounting and Auditing" ); } printMeeting(): void { console .log("The Accounting Department meets each Monday at 10am." ); } generateReports(): void { console .log("Generating accounting reports..." ); } } let department: Department; department = new AccountingDepartment(); department.printName(); department.printMeeting(); department.generateReports(); department.width; department.height;
语法进阶 联合类型 和 类型保护 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 interface Bird { fly: boolean ; sing: () => {}; } interface Dog { fly: boolean ; bark: () => {}; } function trainAnial (animal: Bird | Dog ) { animal.sing(); if (animal.fly) { (animal as Bird).sing(); } else { (animal as Dog).bark(); } } function trainAnialSecond (animal: Bird | Dog ) { if ("sing" in animal) { animal.sing(); } else { animal.bark(); } } class NumberObj { count: number = 123 ; } function addSecond (first: object | NumberObj, second: object | NumberObj ) { if (first instanceof NumberObj && second instanceof NumberObj) { return first.count | second.count; } return 0 ; }
元组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 enum Status { OFFLINE =1 , ONLINE, DELETED } console .log(Status)╰─ npx ts-node ./src/enum .ts { '1' : 'OFFLINE' , '2' : 'ONLINE' , '3' : 'DELETED' , OFFLINE: 1 , ONLINE: 2 , DELETED: 3 }
如果下面的设置为 5,则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 enum Status { OFFLINE, ONLINE = 5 , DELETED, } console .log(Status);╰─ npx ts-node ./src/enum .ts { '0' : 'OFFLINE' , '5' : 'ONLINE' , '6' : 'DELETED' , OFFLINE: 0 , ONLINE: 5 , DELETED: 6 }
实际用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 enum Status { OFFLINE, ONLINE, DELETED, } function getResult (status: Status ) { if (status === Status.OFFLINE) { return "offline" ; } else if (status === Status.ONLINE) { return "online" ; } else if (status === Status.DELETED) { return "deleted" ; } } const result = getResult(1 ); console .log(result);
泛型 generic 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function join <T , P >(first: T, second: P ) { return `${first} ${second} ` ; } function anotherJoin <T , P >(first: T, second: P ): T { return first; } function map <T >(params: Array <T> ) { return params; } join<number , string >(1 , "1" ); join(1 , "1" ); map<string >(["123" ]);
命名空间 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 namespace Home { class Header { constructor ( ) { const elem = document .createElement("div" ); elem.innerText = "This is Header" ; document .body.appendChild(elem); } } class Content { constructor ( ) { const elem = document .createElement("div" ); elem.innerText = "This is Content" ; document .body.appendChild(elem); } } export class Page { constructor ( ) { new Header(); new Content(); } } } new Home.Page();
文件划分 把多文件组合成一个文件输出
1 "outFile": "./build/page.js"
components.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 namespace Components { export namespace SubComponents { export class Test {} } export interface User { name: string ; } export class Header { constructor ( ) { const elem = document .createElement("div" ); elem.innerText = "This is Header" ; document .body.appendChild(elem); } } export class Content { constructor ( ) { const elem = document .createElement("div" ); elem.innerText = "This is Header" ; document .body.appendChild(elem); } } export class Footer { constructor ( ) { const elem = document .createElement("div" ); elem.innerText = "This is Header" ; document .body.appendChild(elem); } } }
page.ts
reference 引入相应的 ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 namespace Home { export class Pages { user: Components.User = { name: "dell" , }; constructor ( ) { new Components.Header(); new Components.Content(); new Components.Footer(); } } }
全局变量 和 手写类型定义文件 d.ts 先导入 jquery<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
1 2 3 4 $(function ( ) { $("body" ).html("123" ); new $.fn.init(); });
参数类型不一致使函数的重载,以及对象进行类型定义
1 2 3 4 5 6 7 8 9 10 11 declare function $ (readyFunc: () => void ): void ;declare function $ (selector: string ): JqueryInstance ;declare namespace $ { namespace fn { class init {} } }
使用 interface 的语法进行函数的重载, 如果$是类的话就不好写
1 2 3 4 5 6 7 interface JQuery { (readyFunc: () => void ): void ; (selector: string ): JqueryInstance; } declare var $: JQuery;
通过 npm install jquery -S 安装的识别出
使用import $ from 'jquery'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 declare module "jquery" { interface JqueryInstance { html: (html: string ) => JqueryInstance; } function $ (readyFunc: () => void ): void ; function $ (selector: string ): JqueryInstance ; namespace $ { namespace fn { class init {} } } export = $; }
keyof 和 泛型指定类型 使用泛型可以让 TS 推断出 test 值的类型
<T extends keyof Person>
T 为 Person 中的键名, Person[T]则为 Person 中的值
getInfo
就可以随着形参的改变而计算出值的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 interface Person { name: string ; age: 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: "lee" , age: 18 , gender: "male" , }); const test = teacher.getInfo("name" );console .log(test);
类型可用指定字符串的值,abc
只能是 name
1 2 type NAME = "name" ;const abc: NAME = "name" ;
装饰器 打开这两个选项开启装饰器功能
1 2 3 4 5 { "experimentalDecorators" : true , "emitDecoratorMetadata" : true , }
类的装饰器 简单写法 1 2 3 4 5 类的装饰器 装饰器本身是一个函数 类装饰器接受的参数是构造函数 装饰器通过 @ 符号来使用 可以使用多个装饰器,从右往左执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function testDecorator (flag: boolean ) { if (flag) { return function (constructor: any ) { constructor .prototype .getName = ( ) => { console .log("dell" ); }; }; } else { return function (constructor: any ) {}; } } @testDecorator (true )class Test {}const test = new Test();(test as any ).getName();
合理写法 不推断出装饰器内部的方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function testDecorator <T extends new (...args: any [] ) => any >(constructor: T ) { return class extends constructor { name = "lee" ; getName ( ) { return this .name; } }; } @testDecorator class Test { name: string ; constructor (name: string ) { this .name = name; } } const test = new Test("dell" );console .log((test as any ).getName());
推断出装饰器内部的方法,写法有改变 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function testDecorator ( ) { return function <T extends new (...args: any [] ) => any >(constructor: T ) { return class extends constructor { name = "lee" ; getName ( ) { return this .name; } }; }; } const Test = testDecorator()( class { name: string ; constructor (name: string ) { this .name = name; } } ); const test = new Test("dell" );console .log(test.getName());
方法装饰器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 function getNameDecorator (target: any , key: string , descriptor: PropertyDescriptor ) { descriptor.value = function ( ) { return 'decorator' ; }; } class Test { name: string ; constructor (name: string ) { this .name = name; } @getNameDecorator getName ( ) { return this .name; } } const test = new Test('dell' );console .log(test.getName());
访问器的装饰器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function visitDecorator (target: any , key: string , descriptor: PropertyDescriptor ) { } class Test { private _name: string ; constructor (name: string ) { this ._name = name; } get name () { return this ._name; } @visitDecorator set name (name: string ) { this ._name = name; } } const test = new Test('dell' );test.name = 'dell lee' ; console .log(test.name);
属性的装饰器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 function nameDecorator (target: any , key: string ): any { target[key] = 'lee' ; } class Test { @nameDecorator name = 'Dell' ; } const test = new Test();console .log((test as any ).name); console .log((test as any ).__proto__.name);
参数装饰器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 function paramDecorator (target: any , method: string , paramIndex: number ) { console .log(target, method, paramIndex); } class Test { getInfo (name: string , @paramDecorator age: number ) { console .log(name, age); } } const test = new Test();test.getInfo('Dell' , 30 );
使用案例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 const userInfo: any = undefined ;function catchError (msg: string ) { return function (target: any , key: string , descriptor: PropertyDescriptor ) { const fn = descriptor.value; descriptor.value = function ( ) { try { fn(); } catch (e) { console .log(msg); } }; }; } class Test { @catchError ('userInfo.name 不存在' ) getName ( ) { return userInfo.name; } @catchError ('userInfo.age 不存在' ) getAge ( ) { return userInfo.age; } } const test = new Test();test.getName(); test.getAge();
Reflect Metadata 是 ES7 的一个提案,它主要用来在声明的时候添加和读取元数据。TypeScript 在 1.5+ 的版本已经支持它,你只需要:
npm i reflect-metadata --save
tsconfig.json
里配置 emitDecoratorMetadata
选项
API
1 2 3 4 5 6 7 function metadata ( metadataKey: any , metadataValue: any ): { (target: Function ): void ; (target: Object , propertyKey : string | symbol): void ; };
使用
1 2 3 4 5 6 7 8 9 10 @Reflect .metadata('inClass' , 'A' )class Test { @Reflect .metadata('inMethod' , 'B' ) public hello(): string { return 'hello world' ; } } console .log(Reflect .getMetadata('inClass' , Test)); console .log(Reflect .getMetadata('inMethod' , new Test(), 'hello' ));
获取类型信息 通过使用 Reflect.getMetadata API,Prop Decorator 能获取属性类型传至 Vue,
1 2 3 4 5 6 7 8 9 10 11 12 13 function Prop ( ): PropertyDecorator { return (target, key: string ) => { const type = Reflect .getMetadata('design:type' , target, key); console .log(`${key} type: ${type .name} ` ); }; } class SomeClass { @Prop () public Aprop!: string ; }
除能获取类型信息外,常用于自定义 metadataKey
,并在合适的时机获取它的值,示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function classDecorator ( ): ClassDecorator { return target => { Reflect .defineMetadata('classMetaData' , 'a' , target); }; } function methodDecorator ( ): MethodDecorator { return (target, key, descriptor ) => { Reflect .defineMetadata('methodMetaData' , 'b' , target, key); }; } @classDecorator ()class SomeClass { @methodDecorator () someMethod ( ) {} } Reflect .getMetadata('classMetaData' , SomeClass); Reflect .getMetadata('methodMetaData' , new SomeClass(), 'someMethod' );
Basic Types 官网 Boolean 1 let isDone: boolean = false ;
Number 1 2 3 4 let decimal: number = 6 ;let hex: number = 0xf00d ;let binary: number = 0b1010 ;let octal: number = 0o744 ;
String 1 2 let color: string = "blue" ;color = "red" ;
Array 1 2 let list: number [] = [1 , 2 , 3 ];let list: Array <number > = [1 , 2 , 3 ];
Tuple 1 2 3 4 5 6 let x: [string , number ];x = ["hello" , 10 ]; x = [10 , "hello" ];
当访问具有已知索引的元素时,将检索正确的类型:
1 2 console .log(x[0 ].substring(1 )); console .log(x[1 ].substring(1 ));
访问一组已知索引之外的元素失败,并显示以下错误:
1 2 x[3 ] = "world" ; console .log(x[5 ].toString());
Enum 1 2 3 enum Color {Red, Green, Blue}let c: Color = Color.Green;
默认情况下,枚举从 0 开始对其成员编号。您可以通过手动设置其成员之一的值来更改此值。 例如,我们可以从 1 而不是 0 开始上一个示例:
1 2 3 4 5 6 enum Color { Red = 1 , Green, Blue, } let c: Color = Color.Green;
甚至手动设置枚举中的所有值:
1 2 3 4 5 6 enum Color { Red = 1 , Green = 2 , Blue = 4 , } let c: Color = Color.Green;
枚举的一个方便功能是,您也可以在枚举中从数字值转到该值的名称。 例如,如果我们具有值 2,但不确定上面的 Color 枚举中映射到的值,则可以查找对应的名称:
1 2 3 4 5 6 7 8 enum Color { Red = 1 , Green, Blue, } let colorName: string = Color[2 ];console .log(colorName);
Any 我们可能需要描述编写应用程序时不知道的变量类型。 这些值可能来自动态内容,例如 来自用户或第三方库。 在这些情况下,我们要选择退出类型检查,并让值通过编译时检查。 为此,我们将它们标记为任何类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let notSure: any = 4 ;notSure = "maybe a string instead" ; notSure = false ; let notSure: any = 4 ;notSure.ifItExists(); notSure.toFixed(); let prettySure: Object = 4 ;prettySure.toFixed(); let list: any [] = [1 , true , "free" ];list[1 ] = 100 ;
Void 1 2 3 function warnUser ( ): void { console .log("This is my warning message" ); }