🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 参考文档 - [TypeScript 官方文档](https://www.tslang.cn/docs/handbook/basic-types.html) - [TypeScript 入门教程](https://ts.xcatliu.com/introduction/get-typescript) # 安装与配置(VSCode) 1)全局安装: ``` npm install -g typescript ``` 2)VSCode 配置: 3)编译单个文件:运行`tsc filename.ts`即可对 .ts 文件进行编译生成对应的 .js 文件。 # 原始数据类型 JavaScript 的数据类型分为两种:原始数据类型(Primitive data types)和对象类型(Object types)。 原始数据类型包括:布尔值、数值、字符串、null、undefined、Symbol(ES6)。 ```js let isDone: boolean = false let age: number = 20 let firstName: string = 'name' let message: string = `Hello, ${firstName}` // 模板字符串 let u: undefined = undefined let n: null = null // undefined and null 是所有数据类型的子类型 // 例如 undefined 类型的变量可以赋值给 number 类型的变量 let num: number = undefined // 空值:JavaScirpt 没有空值(Void)的概念,在 TypeScript 中可以使用 void 表示没有任何返回值的函数 function alertName(): void { alert('My name is Tom') } ``` # 任意值(Any) Any 用来表示允许赋值为任意类型。 ```js let myFavoriteNumber: string = 'seven' myFavoriteNumber = 7 // error TS2322: Type '7' is not assignable to type 'string'. let myFavoriteNumber: any = 'seven' myFavoriteNumber = 7 // √ ``` 在任意值上访问任何属性都是允许的,也可以调用任何方法。声明一个变量为任意值后,对它的任何操作,返回的内容的类型都为任意值。 变量如果在声明时未指定其类型,那么其会被识别为任意值类型。 # 类型推论(Type Inference) 如果没有明确的指定类型,那么 TypeScript 会依照类型推论的规则推断出一个类型。 如果定义的时候没有赋值,不管之后有没有赋值,变量都会被推断成`any`类型而完全不被类型检查。 ```js let myFavoriteNumber myFavoriteNumber = 'seven' myFavoriteNumber = 7 ``` # 联合类型(Union Types) 联合类型表示取值可以为多种类型中的一种。联合类型使用`|`分隔每个类型。 ```js let myFavoriteNumber: string | number myFavoriteNumber = 'seven' myFavoriteNumber = 7 ``` 当 TypeScript 不确定一个联合类型的变量到底是哪个类型时。我们只能访问此联合类型的所有类型里共有的属性或方法: ``` function getLength(something: string | number): number { return something.length } // index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'. // Property 'length' does not exist on type 'number'. ``` `length`不是`string`和`number`的共有属性,所以会报错。 联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型: ```js let myFavoriteNumber: string | number myFavoriteNumber = 'seven' console.log(myFavoriteNumber.length) // 5 myFavoriteNumber = 7 console.log(myFavoriteNumber.length) // 编译时报错 // index.ts(5,30): error TS2339: Property 'length' does not exist on type 'number'. ``` 上例中,第二行的`myFavoriteNumber`被推断成了`string`,访问其`length`属性不会报错。 而第四行的`myFavoriteNumber`被推断成了`number`,访问其`length`属性时就会报错了。 # 对象的类型——接口 TypeScript 使用接口(Interfaces)来定义对象的类型。 在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。 TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对对象的形状(Shape)进行描述。 ``` interface Person { // 接口一般首字母大写 name: string; age: number; } let tom: Person = { name: 'Tom', age: 25 } ``` 上面的例子中,我们定义了一个接口`Person`,然后定义了一个变量`tom`,它的类型是`Person`。这样,我们就约束了`tom`的形状必须与接口`Person`一致。 定义的变量比接口少一些属性或者多一些属性都是不允许的,赋值的时候变量的形状必须与接口的形状保持一致。 ## 可选属性和任意属性 有时我们希望不要完全匹配一个形状,那么可以用可选属性: ```js interface Person { name: string; age?: number; // 该属性可以不存在 } let tom: Person = { name: 'Tom' } ``` 有时我们希望一个接口允许有任意的属性,可以使用如下方式: ```js interface Person { name: string; age?: number; [propName: string]: any; } let tom: Person = { name: 'Tom', gender: 'male' } ``` 使用`[propName: string]`定义了任意属性取`string`类型的值。 需要注意的是,**一个接口只允许定义一个任意属性,且一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集:** ```js interface Person { name: string; age?: number; [propName: string]: string; // wrong [propName: string]: number; // wrong [propName: string]: string | number; // right } ``` ## 只读属性 有时我们希望对象的一些字段只能在创建的时候被赋值,那么可以用`readonly`定义只读属性: ```js interface Person { readonly id: number; name: string; age?: number; [propName: string]: any; } let tom: Person = { id: 12345, name: 'Tom', gender: 'male' } tom.id = 9527 // Cannot assign to 'id' because it is a read-only property. ``` 注意,**只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候。** # 数组的类型 在 TypeScript 中,数组类型有多种定义方式,比较灵活。 1)[类型+方括号] 表示法: ```js let fibonacci: number[] = [1, 1, 2, 3, 5] ``` 数组中的项不允许出现其他类型,且数组的一些方法的参数也会根据数组在定义时约定的类型进行限制: ```js let fibonacci: number[] = [1, '1', 2, 3, 5] // Type 'string' is not assignable to type 'number'. ``` ```js let fibonacci: number[] = [1, 1, 2, 3, 5] fibonacci.push('8') // Argument of type '"8"' is not assignable to parameter of type 'number'. ``` 2)数组泛型(Array Generic):使用`Array<elemType>`的形式来表示数组: ```js let fibonacci: Array<number> = [1, 1, 2, 3, 5] ``` 3)用接口表示数组: ```js interface NumberArray { [index: number]: number; } let fibonacci: NumberArray = [1, 1, 2, 3, 5] ``` `NumberArray`表示:只要索引的类型是数字时,那么值的类型必须是数字。 虽然接口也可以用来描述数组,但是我们一般不会这么做,因为这种方式比前两种方式复杂多了。但是其常用于表示类数组。 # 函数的类型 在 JavaScript 中,有两种常见的定义函数的方式——函数声明和函数表达式: ```js // 函数声明(Function Declaration) function sum (x, y) { return x + y } // 函数表达式(Function Expression) let mySum = function (x, y) { return x + y }; ``` 在 TypeScript 中函数声明的形式如下,需要对输入和输出进行约束: ```ts function sum(x: number, y: number): number { return x + y } ``` 定义一个函数表达式的形式如下: ```ts let mySum = function (x: number, y: number): number { return x + y } ``` 这样是可以通过编译的,但是上面的代码只对等号右侧的匿名函数进行了类型定义,而等号左边的`mySum`是通过赋值操作进行类型推论得出来的。如果要手动给`mySum`添加类型,则应该这样: ```ts let mySum: (x: number, y: number) => number = function (x: number, y: number): number { return x + y } ``` 注意这里的`=>`与 ES6 中的`=>`不同,在 TypeScript 的类型定义中,`=>`表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。 ## 用接口定义函数的形状 我们也可以使用接口的方式来定义一个函数需要符合的形状: ```ts interface SearchFunc { (source: string, subString: string): boolean; } let mySearch: SearchFunc; mySearch = function(source: string, subString: string) { return source.search(subString) !== -1; } ``` 采用函数表达式|接口定义函数的方式时,对等号左侧进行类型限制,可以保证以后对函数名赋值时保证参数个数、参数类型、返回值类型不变。 ## 可选参数 与接口的可选属性类似,我们用`?`表示可选的参数: ```ts function buildName(firstName: string, lastName?: string) { if (lastName) { return firstName + ' ' + lastName; } else { return firstName; } } let tomcat = buildName('Tom', 'Cat'); let tom = buildName('Tom'); ``` 需要注意的是,可选参数必须接在必需参数后面。换句话说,**可选参数后面不允许再出现必需参数了**。 ## 参数默认值 在 ES6 中,我们允许给函数的参数添加默认值,**TypeScript 会将添加了默认值的参数识别为可选参数**: ```ts function buildName(firstName: string, lastName: string = 'Cat') { return firstName + ' ' + lastName; } let tomcat = buildName('Tom', 'Cat'); let tom = buildName('Tom'); ``` 此时就不受「可选参数必须接在必需参数后面」的限制了: ```ts function buildName(firstName: string = 'Tom', lastName: string) { return firstName + ' ' + lastName; } let tomcat = buildName('Tom', 'Cat'); let cat = buildName(undefined, 'Cat'); ``` ## 剩余参数 ES6 中,可以使用`...rest`的方式获取函数中的剩余参数(rest 参数): ```js function push(array, ...items) { items.forEach(function(item) { array.push(item); }); } let a: any[] = []; push(a, 1, 2, 3); ``` 事实上,`items`是一个数组。所以我们可以用数组的类型来定义它: ```ts function push(array: any[], ...items: any[]) { items.forEach(function(item) { array.push(item); }); } let a = []; push(a, 1, 2, 3); ``` 注意,rest 参数只能是最后一个参数。 # 类型断言(Type Assertion) 类型断言可以用来手动指定一个值的类型。一般使用`值 as 类型`的语法。类型断言的常见用途有以下几种: 1)将一个联合类型断言为其中一个类型 当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型中共有的属性和方法: ```ts interface Cat { name: string; run(): void; } interface Fish { name: string; swim(): void; } function getName(animal: Cat | Fish) { return animal.name; } ``` 而有时我们确实需要在不确定类型的时候就访问其中一个类型特有的属性或方法,比如: