教程 https://www.typescriptlang.org/docs/home.html https://ts.xcatliu.com https://zhongsp.gitbook.io/typescript-handbook/tutorials
参考文档
安装TypeScript 1 npm install -g typescript
Hello TS 1 2 3 4 5 6 function sayHello (person: string ) { return 'Hello, ' + person; } let user = 'Tom' ;console .log(sayHello(user));
原始数据类型
boolean
number
null
undefined
1 2 3 4 5 let isDone: boolean = false ;let createdByNewBoolean: boolean = new Boolean (1 );let createdByBoolean: boolean = Boolean (1 );
1 2 3 4 5 6 7 8 let decLiteral: number = 6 ;let hexLiteral: number = 0xf00d ;let binaryLiteral: number = 0b1010 ;let octalLiteral: number = 0o744 ;let notANumber: number = NaN ;let infinityNumber: number = Infinity ;
1 2 3 4 5 let myName: string = 'Tom' ;let myAge: number = 25 ;let sentence: string = `Hello, my name is ${myName} . I'll be ${myAge + 1 } years old next month.` ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let unusable: void = undefined ;let u: undefined = undefined ;let n: null = null ;let num: number = undefined ;let u: undefined ;let num: number = u;let u: void ;let num: number = u;
任意值 在任意值上访问任何属性都是允许的
1 2 let myFavoriteNumber: any = 'seven' ;myFavoriteNumber = 7 ;
也允许调用任何方法
1 2 3 4 5 6 7 8 let anyThing: any = 'hello' ;console .log(anyThing.myName);console .log(anyThing.myName.firstName);let anyThing: any = 'Tom' ;anyThing.setName('Jerry' ); anyThing.setName('Jerry' ).sayHello(); anyThing.myName.setFirstName('Cat' );
变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型
1 2 3 4 let something;something = 'seven' ; something = 7 ; something.setName('Tom' );
类型推论 TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论。 如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查。
1 2 let myFavoriteNumber = 'seven' ;myFavoriteNumber = 7 ;
联合类型 联合类型(Union Types)表示取值可以为多种类型中的一种。
1 2 3 let myFavoriteNumber: string | number ;myFavoriteNumber = 'seven' ; myFavoriteNumber = 7 ;
1 2 3 4 function getLength (something: string | number ): number { return something.length; }
接口 1 2 3 4 5 6 7 8 9 interface Person { name: string ; age: number ; } let tom: Person = { name: 'Tom' , age: 25 };
可选属性的含义是该属性可以不存在
1 2 3 4 5 6 7 8 interface Person { name: string ; age?: number ; } let tom: Person = { name: 'Tom' };
有时候我们希望一个接口允许有任意的属性
1 2 3 4 5 6 7 8 9 10 interface Person { name: string ; age?: number ; [propName: string ]: any ; } let tom: Person = { name: 'Tom' , gender: 'male' };
一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
1 2 3 4 5 6 7 8 9 10 11 12 interface Person { name: string ; age?: number ; [propName: string ]: string ; } let tom: Person = { name: 'Tom' , age: 25 , gender: 'male' };
有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用readonly
定义只读属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 interface Person { readonly id: number ; name: string ; age?: number ; [propName: string ]: any ; } let tom: Person = { id: 89757 , name: 'Tom' , gender: 'male' }; tom.id = 9527 ;
数组的类型 1 let fibonacci: number [] = [1 , 1 , 2 , 3 , 5 ];
数组的项中不允许出现其他的类型
数组泛型
1 let fibonacci: Array <number > = [1 , 1 , 2 , 3 , 5 ];
用接口表示数组 虽然接口也可以用来描述数组,但是我们一般不会这么做,因为这种方式比前两种方式复杂多了。
1 2 3 4 interface NumberArray { [index: number ]: number ; } let fibonacci: NumberArray = [1 , 1 , 2 , 3 , 5 ];
类数组
1 2 3 function sum ( ) { let args: number [] = arguments ; }
any在数组中的应用
1 let list: any [] = ['xcatliu' , 25 , { website : 'http://xcatliu.com' }];
函数的类型 1 2 3 4 function sum (x: number , y: number ): number { return x + y; }
1 2 3 4 let mySum: (x: number , y: number ) => number = function (x: number , y: number ): number { return x + y; };
在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。
用接口定义函数的形状
1 2 3 4 5 6 7 8 interface SearchFunc { (source: string , subString : string ): boolean ; } let mySearch: SearchFunc;mySearch = function (source: string , subString: string ) { return source.search(subString) !== -1 ; }
可选参数 可选参数后面不允许再出现必需参数了
1 2 3 4 5 6 7 8 9 function buildName (firstName: string , lastName?: string ) { if (lastName) { return firstName + ' ' + lastName; } else { return firstName; } } let tomcat = buildName('Tom' , 'Cat' );let tom = buildName('Tom' );
参数默认值
1 2 3 4 5 function buildName (firstName: string , lastName: string = 'Cat' ) { return firstName + ' ' + lastName; } let tomcat = buildName('Tom' , 'Cat' );let tom = buildName('Tom' );
类型断言 类型断言(Type Assertion)可以用来手动指定一个值的类型。
类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的:
1 2 3 4 5 6 7 function getLength (something: string | number ): number { if ((<string >something).length) { return (<string >something).length; } else { return something.toString().length; } }
声明文件
declare var 声明全局变量
declare function 声明全局方法
declare class 声明全局类
declare enum 声明全局枚举类型
declare namespace 声明(含有子属性的)全局对象
interface 和 type 声明全局类型
export 导出变量
export namespace 导出(含有子属性的)对象
export default ES6 默认导出
export = commonjs 导出模块
export as namespace UMD 库声明全局变量
declare global 扩展全局变量
declare module 扩展模块
/// 三斜线指令
npm包的声明文件可能存在于两个地方: -与该 npm 包绑定在一起。判断依据是 package.json 中有 types 字段,或者有一个 index.d.ts 声明文件。这种模式不需要额外安装其他包,是最为推荐的,所以以后我们自己创建 npm 包的时候,最好也将声明文件与 npm 包绑定在一起。
发布到 @types 里。我们只需要尝试安装一下对应的 @types 包就知道是否存在该声明文件,安装命令是 npm install @types/foo –save-dev。这种模式一般是由于 npm 包的维护者没有提供声明文件,所以只能由其他人将声明文件发布到 @types 里了。
npm 包的声明文件主要有以下几种语法:
export 导出变量
export namespace 导出(含有子属性的)对象
export default ES6 默认导出
export = commonjs 导出模块
内置对象
Boolean
Error
Date
RegExp
类型别名 1 2 3 4 5 6 7 8 9 10 type Name = string ;type NameResolver = () => string ;type NameOrResolver = Name | NameResolver;function getName (n: NameOrResolver ): Name { if (typeof n === 'string' ) { return n; } else { return n(); } }
字符串字面量类型 字符串字面量类型用来约束取值只能是某几个字符串中的一个。
1 2 3 4 5 6 7 type EventNames = 'click' | 'scroll' | 'mousemove' ;function handleEvent (ele: Element, event: EventNames ) { } handleEvent(document .getElementById('hello' ), 'scroll' ); handleEvent(document .getElementById('world' ), 'dbclick' );
元组 数组合并了相同类型的对象,而元组(Tuple)合并了不同类型的对象。
1 2 3 4 5 6 7 8 let tom: [string , number ] = ['Tom' , 25 ];let tom: [string , number ];tom[0 ] = 'Tom' ; tom[1 ] = 25 ; tom[0 ].slice(1 ); tom[1 ].toFixed(2 );
枚举 1 2 3 4 5 6 7 8 9 10 11 enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};console .log(Days["Sun" ] === 0 ); console .log(Days["Mon" ] === 1 ); console .log(Days["Tue" ] === 2 ); console .log(Days["Sat" ] === 6 ); console .log(Days[0 ] === "Sun" ); console .log(Days[1 ] === "Mon" ); console .log(Days[2 ] === "Tue" ); console .log(Days[6 ] === "Sat" );
手动赋值
1 2 3 4 5 6 enum Days {Sun = 7 , Mon = 1 , Tue, Wed, Thu, Fri, Sat};console .log(Days["Sun" ] === 7 ); console .log(Days["Mon" ] === 1 ); console .log(Days["Tue" ] === 2 ); console .log(Days["Sat" ] === 6 );
类 public private 和 protected 1 2 3 4 5 6 7 8 9 10 11 class Animal { public name; public constructor (name ) { this .name = name; } } let a = new Animal('Jack' );console .log(a.name); a.name = 'Tom' ; console .log(a.name);
参数属性 1 2 3 4 5 6 class Animal { public constructor (public name ) { } }
readonly 1 2 3 4 5 6 7 8 9 10 11 12 class Animal { readonly name; public constructor (name ) { this .name = name; } } let a = new Animal('Jack' );console .log(a.name); a.name = 'Tom' ;
抽象类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 abstract class Animal { public name; public constructor (name ) { this .name = name; } public abstract sayHi(); } class Cat extends Animal { public sayHi ( ) { console .log(`Meow, My name is ${this .name} ` ); } } let cat = new Cat('Tom' );
类的类型 1 2 3 4 5 6 7 8 9 10 11 12 class Animal { name: string ; constructor (name: string ) { this .name = name; } sayHi(): string { return `My name is ${this .name} ` ; } } let a: Animal = new Animal('Jack' );console .log(a.sayHi());
类与接口 类实现接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 interface Alarm { alert(); } class Door {} class SecurityDoor extends Door implements Alarm { alert ( ) { console .log('SecurityDoor alert' ); } } class Car implements Alarm { alert ( ) { console .log('Car alert' ); } }
一个类可以实现多个接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 interface Alarm { alert(); } interface Light { lightOn(); lightOff(); } class Car implements Alarm , Light { alert ( ) { console .log('Car alert' ); } lightOn ( ) { console .log('Car light on' ); } lightOff ( ) { console .log('Car light off' ); } }
接口继承接口 1 2 3 4 5 6 7 8 interface Alarm { alert(); } interface LightableAlarm extends Alarm { lightOn(); lightOff(); }
接口继承类 1 2 3 4 5 6 7 8 9 10 class Point { x: number ; y: number ; } interface Point3d extends Point { z: number ; } let point3d: Point3d = {x : 1 , y : 2 , z : 3 };
泛型 泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
1 2 3 4 5 6 7 8 9 function createArray (length: number , value: any ): Array <any > { let result = []; for (let i = 0 ; i < length; i++) { result[i] = value; } return result; } createArray(3 , 'x' );
例子 1 2 3 4 5 6 7 8 9 function createArray <T >(length: number , value: T ): Array <T > { let result: T[] = []; for (let i = 0 ; i < length; i++) { result[i] = value; } return result; } createArray<string >(3 , 'x' );
自动推算 1 2 3 4 5 6 7 8 9 function createArray <T >(length: number , value: T ): Array <T > { let result: T[] = []; for (let i = 0 ; i < length; i++) { result[i] = value; } return result; } createArray(3 , 'x' );
多个类型参数 1 2 3 4 5 function swap <T , U >(tuple: [T, U] ): [U , T ] { return [tuple[1 ], tuple[0 ]]; } swap([7 , 'seven' ]);
泛型接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 interface CreateArrayFunc<T> { (length: number , value : T): Array <T>; } let createArray: CreateArrayFunc<any >;createArray = function <T >(length: number , value: T ): Array <T > { let result: T[] = []; for (let i = 0 ; i < length; i++) { result[i] = value; } return result; } createArray(3 , 'x' );
泛型类 1 2 3 4 5 6 7 8 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; };
声明合并
代码检查 由于 ESLint 默认使用 Espree 进行语法解析,无法识别 TypeScript 的一些语法,故我们需要安装 @typescript-eslint/parser,替代掉默认的解析器
1 2 npm install --save-dev eslint npm install --save-dev typescript @typescript -eslint/parser
.eslintrc.js配置
1 2 3 4 5 6 7 8 9 10 11 12 13 module .exports = { parser: '@typescript-eslint/parser' , plugins: ['@typescript-eslint' ], rules: { 'no-var' : "error" , '@typescript-eslint/consistent-type-definitions' : [ "error" , "interface" ] } }
手动检查一个ts文件
1 ./node_modules/.bin/eslint index.ts
自动检查一个文件
1 2 3 4 5 6 vi package.json { "scripts": { "eslint": "eslint index.ts" } }
检查整个项目的ts文件 vi package.json
1 2 3 4 5 { "scripts" : { "eslint" : "eslint src --ext .ts" } }
使用Prettier修复格式错误 1 npm install --save-dev prettier
prettier.config.js配置
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 module .exports = { printWidth: 100 , tabWidth: 4 , useTabs: false , semi: true , singleQuote: true , quoteProps: 'as-needed' , jsxSingleQuote: false , trailingComma: 'none' , bracketSpacing: true , jsxBracketSameLine: false , arrowParens: 'always' , rangeStart: 0 , rangeEnd: Infinity , requirePragma: false , insertPragma: false , proseWrap: 'preserve' , htmlWhitespaceSensitivity: 'css' , endOfLine: 'lf' };
修改 .vscode/settings.json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 { "files.eol" : "\n" , "editor.tabSize" : 4 , "editor.formatOnSave" : true , "editor.defaultFormatter" : "esbenp.prettier-vscode" , "eslint.autoFixOnSave" : true , "eslint.validate" : [ "javascript" , "javascriptreact" , { "language" : "typescript" , "autoFix" : true } ], "typescript.tsdk" : "node_modules/typescript/lib" }
TS编码规范
Lodash库 https://www.lodashjs.com/