数据类型
原始数据类型包括:布尔值
、数值
、字符串
、null
、undefined
、Symbol
布尔值-boolean
在TypeScript
中,使用boolean
定义布尔值类型
在TypeScript
中,boolean
是JavaScript
中的基本类型,而Boolean
是JavaScript
中的构造函数
使用构造函数Boolean
创造的对象不是布尔值
let isDone: boolean = false;
let createdByNewBoolean: boolean = new Boolean(1);
// Type 'Boolean' is not assignable to type 'boolean'.
// 'boolean' is a primitive, but 'Boolean' is a wrapper object. Prefer using 'boolean' when possible.
let createdByNewBoolean: Boolean = new Boolean(1); // new Boolean() 返回的是一个 Boolean 对象,不是 boolean 类型
let createdByBoolean: boolean = Boolean(1); // 直接调用 Boolean 也可以返回一个 boolean 类型
数值-number
使用number
定义数值类型
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
// ES6 中的二进制表示法,会被编译成十进制数字
let binaryLiteral: number = 0b1010; // 编译后 var binaryLiteral = 10;
// ES6 中的八进制表示法,会被编译成十进制数字
let octalLiteral: number = 0o744; // 编译后 var octalLiteral = 484;
let notANumber: number = NaN;
let infinityNumber: number = Infinity;
字符串-string
使用string
定义字符串类型
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.`;
空值-void
JavaScript
没有空值(Void
)的概念,在TypeScript
中,可以用void
表示没有任何返回值的函数
声明一个void
类型的变量没有什么用,因为你只能将它赋值为undefined
和null
function alertName(): void {
alert('My name is Tom');
}
let unusable: void = undefined;
Null & Undefined
在TypeScript
中,可以使用null
和undefined
来定义这两个原始数据类型
与void
的区别是:undefined
和null
是所有类型的子类型。也就是说undefined
类型的变量,可以赋值给number
类型的变量。而void
类型的变量不能赋值给number
类型的变量
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;
// Type 'void' is not assignable to type 'number'.
任意值-any
- 任意值(
Any
)用来表示允许赋值为任意类型 - 如果是一个普通类型,在赋值过程中改变类型是不被允许的。但如果是
any
类型,则允许被赋值为任意类型 - 在任意值上访问任何属性都是允许的;也允许调用任何方法
- 声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值
- 变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型
let myFavoriteNumber: string = 'seven';
myFavoriteNumber = 7;
// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.
let myFavoriteNumber: any = 'seven';
myFavoriteNumber = 7;
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');
let something; // 等价于 let something: any;
something = 'seven';
something = 7;
something.setName('Tom');
类型推论
如果没有明确的指定类型,那么TypeScript
会依照类型推论(Type Inference
)的规则推断出一个类型
如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成any
类型而完全不被类型检查
let myFavoriteNumber = 'seven'; // let myFavoriteNumber: string = 'seven'; myFavoriteNumber 推断是 string 类型
myFavoriteNumber = 7;
// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.
let myFavoriteNumber; // 定义的时候没有赋值,推断是 any 类型
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
联合类型-|
联合类型(Union Types
)表示取值可以为多种类型中的一种
联合类型使用 |
分隔每个类型。
let myFavoriteNumber: string | number; // 是 string 或者 number 类型,但不能是其他类型
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
myFavoriteNumber = true;
// index.ts(2,1): error TS2322: Type 'boolean' is not assignable to type 'string | number'.
// Type 'boolean' is not assignable to type 'number'.
当TypeScript
不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法
联合类型的变量在被赋值的时候,会根据类型推论的规则推断出一个类型
function getLength(something: string | number): number {
return something.length; // length 不是 string 和 number 的共有属性,所以报错
}
// index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.
// Property 'length' does not exist on type 'number'.
function getString(something: string | number): string {
return something.toString(); // 访问 string 和 number 的共有属性没有问题
}
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven'; // 被赋值,此时推断是 string 类型
console.log(myFavoriteNumber.length); // 5
myFavoriteNumber = 7; // 被赋值,此时推断是 number 类型
console.log(myFavoriteNumber.length); // 编译时报错。因为 number 类型没有 length 属性
// index.ts(5,30): error TS2339: Property 'length' does not exist on type 'number'.
对象的类型-interface
- 在
TypeScript
中,我们使用接口(Interfaces
)来定义对象的类型 - 接口一般首字母大写。有的编程语言中会建议接口的名称加上
I
前缀 - 赋值的时候,变量的形状必须和接口的形状保持一致
interface Person { // 定义了一个接口 Person
name: string;
age: number;
}
let tom: Person = { // 定义了一个变量,类型是 Person。 tom 的形状必须和接口 Person 一致
name: 'Tom',
age: 25
};
- 定义的变量比接口少了一些属性是不允许的
interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom'
};
// index.ts(6,5): error TS2322: Type '{ name: string; }' is not assignable to type 'Person'.
// Property 'age' is missing in type '{ name: string; }'.
- 多一些属性也是不允许的
interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom',
age: 25,
gender: 'male'
};
// index.ts(9,5): error TS2322: Type '{ name: string; age: number; gender: string; }' is not assignable to type 'Person'.
// Object literal may only specify known properties, and 'gender' does not exist in type 'Person'.
- 可选属性
?
interface Person {
name: string;
age?: number; // 可选属性
}
let tom: Person = {
name: 'Tom'
};
- 任意属性(一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集)
interface Person {
name: string;
age?: number; // 可选属性
[propName: string]: any; // 任意属性(任意属性取 string 类型的值)
}
let tom: Person = {
name: 'Tom',
gender: 'male'
};
- 只读属性
readonly
(只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候)
interface Person {
readonly id: number; // 只读属性,只能在创建的时候被赋值
name: string;
age?: number;
[propName: string]: any;
}
let tom: Person = {
id: 89757,
name: 'Tom',
gender: 'male'
};
tom.id = 9527;
// index.ts(14,5): error TS2540: Cannot assign to 'id' because it is a constant or a read-only property.
数组的类型
「类型 + 方括号」表示法
- 最简单的方法是使用
「类型 + 方括号」
来表示数组 - 数组的项中不允许出现其他的类型
- 数组的一些方法的参数也会根据数组在定义时约定的类型进行限制
let fibonacci: number[] = [1, 1, 2, 3, 5];
let fibonacci: number[] = [1, '1', 2, 3, 5];
// Type 'string' is not assignable to type 'number'.
数组泛型
可以使用数组泛型(Array Generic
) Array<elemType>
来表示数组
let fibonacci: Array<number> = [1, 1, 2, 3, 5];
接口
- 也可以用接口来描述数组
- 虽然接口也可以用来描述数组,但是我们一般不会这么做,因为这种方式比前两种方式复杂多了
- 不过有一种情况例外,那就是它常用来表示类数组
interface NumberArray { // NumberArray 表示:只要索引的类型是数字时,那么值的类型必须是数字。
[index: number]: number;
}
let fibonacci: NumberArray = [1, 1, 2, 3, 5];
类数组
- 类数组(
Array-like Object
)不是数组类型,比如arguments
- 类数组,不能用普通的数组的方式来描述,而应该用接口
- 常用的类数组都有自己的接口定义,如
IArguments
,NodeList
,HTMLCollection
等
function sum() {
let args: number[] = arguments;
}
// Type 'IArguments' is missing the following properties from type 'number[]': pop, push, concat, join, and 24 more.
function sum() {
let args: {
[index: number]: number; // 约束当前索引类型是数字;值的类型是数字
length: number; // 约束 length 是数字
callee: Function; // 约束 callee 是方法
} = arguments;
}
function sum() {
let args: IArguments = arguments; // 直接使用对应的内置对象 IArguments
}
any应用
一个比较常见的做法是,用any
表示数组中允许出现任意类型
let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];
函数的类型
函数声明
// 函数声明(Function Declaration)
function sum(x, y) {
return x + y;
}
// 函数表达式(Function Expression)
let mySum = function (x, y) {
return x + y;
};
一个函数有输入和输出,要在TypeScript
中对其进行约束,需要把输入和输出都考虑到
函数表达式
输入多余的(或者少于要求的)参数,是不被允许的
function sum(x: number, y: number): number {
return x + y;
}
sum(1, 2, 3); // 输入多余的参数,编译错误
// index.ts(4,1): error TS2346: Supplied parameters do not match any signature of call target.
sum(1); // 输入少于要求的参数,编译错误
// index.ts(4,1): error TS2346: Supplied parameters do not match any signature of call target.
函数表达式
let mySum = function (x: number, y: number): number { // mySum 通过赋值操作进行类型推论而推断出来的
return x + y;
};
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
注意不要混淆了TypeScript
中的=>
和ES6
中的=>
。
在TypeScript
的类型定义中,=>
用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型
接口定义函数形状
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
可选参数
- 与接口中的可选属性类似,用
?
表示可选的参数 - 可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必需参数了
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
function buildName(firstName?: string, lastName: string) {
if (firstName) {
return firstName + ' ' + lastName;
} else {
return lastName;
}
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName(undefined, 'Tom');
// index.ts(1,40): error TS1016: A required parameter cannot follow an optional parameter.
参数默认值
TypeScript
会将添加了默认值的参数识别为可选参数- 此时就不受「可选参数必须接在必需参数后面」的限制了
function buildName(firstName: string, lastName: string = 'Cat') {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
function buildName(firstName: string = 'Tom', lastName: string) {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let cat = buildName(undefined, 'Cat');
剩余参数
ES6
中,可以使用...rest
的方式获取函数中的剩余参数(rest
参数)rest
参数只能是最后一个参数
function push(array: any[], ...items: any[]) {
items.forEach(function(item) {
array.push(item);
});
}
let a = [];
push(a, 1, 2, 3);
重载
- 重载允许一个函数接受不同数量或类型的参数时,作出不同的处理
- 可以使用重载定义多个
reverse
的函数类型
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
// 重复定义了多次函数 reverse,前几次都是函数定义,最后一次是函数实现
// TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面
类型断言
类型断言(Type Assertion
)可以用来手动指定一个值的类型
语法:<类型>值
或者 值 as 类型
在需要断言的变量前加上<Type>
即可
类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的
function getLength(something: string | number): number {
if ((<string>something).length) {
return (<string>something).length;
} else {
return something.toString().length;
}
}
声明文件
假如我们想使用第三方库jQuery
,代码中这么使用
$('#foo');
// or
jQuery('#foo');
typescript
中需要使用声明语句declare var
来定义它的类型
declare var jQuery: (selector: string) => any; // 定义了全局变量 JQuery 的类型,仅会用于编译时检查,在编译结果中会别删除
jQuery('#foo');
- 通常我们会把声明语句放到一个单独的文件(
jQuery.d.ts
)中,这就是声明文件
// src/jQuery.d.ts
declare var jQuery: (selector: string) => any;
-
声明文件必需以
.d.ts
为后缀 -
第三方声明文件一般不需要我们自己定义,直接下载下来使用即可。
-
推荐使用
@types
统一管理第三方库的声明文件
npm install @types/jquery --save-dev
-
可以在这个页面搜索需要的声明文件
-
更多声明文件相关参考:TypeScript入门教程-声明文件
内置对象
ECMAScript
标准提供的内置对象有:Boolean
、Error
、Date
、RegExp
等
let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
- DOM 和 BOM 提供的内置对象有:
Document
、HTMLElement
、Event
、NodeList
等
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
// Do something
});
- 当使用一些常用的方法时,
TypeScript
实际上已经帮你做了很多类型判断的工作了
Math.pow(10, '2');
// index.ts(1,14): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
document.addEventListener('click', function(e) {
console.log(e.targetCurrent);
});
// index.ts(2,17): error TS2339: Property 'targetCurrent' does not exist on type 'MouseEvent'.
Node.js
不是内置对象的一部分,如果想用TypeScript
写Node.js
,则需要引入第三方声明文件
npm install @types/node --save-dev
类型别名
使用type
创建类型别名,给一个类型起个新名字
类型别名常用于联合类型
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
字符串字面量类型
字符串字面量类型用来约束取值只能是某几个字符串中的一个
类型别名与字符串字面量类型都是使用type
进行定义
type EventNames = 'click' | 'scroll' | 'mousemove';
function handleEvent(ele: Element, event: EventNames) {
// do something
}
handleEvent(document.getElementById('hello'), 'scroll'); // 没问题
handleEvent(document.getElementById('world'), 'dbclick'); // 报错,event 不能为 'dbclick'
// index.ts(7,47): error TS2345: Argument of type '"dbclick"' is not assignable to parameter of type 'EventNames'.
元组
数组合并了相同类型的对象,而元组(Tuple
)合并了不同类型的对象
// 定义一对值分别为 string 和 number 的元组
let tom: [string, number] = ['Tom', 25];
// 当赋值或访问一个已知索引的元素时,会得到正确的类型
let tom: [string, number];
tom[0] = 'Tom';
tom[1] = 25;
tom[0].slice(1);
tom[1].toFixed(2);
// 也可以只赋值其中一项
let tom: [string, number];
tom[0] = 'Tom';
// 当直接对元组类型的变量进行初始化或者赋值的时候,需要提供所有元组类型中指定的项
let tom: [string, number];
tom = ['Tom', 25];
let tom: [string, number];
tom = ['Tom'];
// Property '1' is missing in type '[string]' but required in type '[string, number]'.
枚举
- 枚举(
Enum
)类型用于取值被限定在一定范围内的场景 - 枚举使用
enum
关键字来定义 - 枚举成员会被赋值为从
0
开始递增的数字,同时也会对枚举值到枚举名进行反向映射
enum Days {Sun, Mon, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 0); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true
console.log(Days[0] === "Sun"); // true
console.log(Days[1] === "Mon"); // true
console.log(Days[2] === "Tue"); // true
console.log(Days[6] === "Sat"); // true
// 手动赋值
enum Days {Sun = 7, Mon = 1, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 7); // true
console.log(Days["Mon"] === 1); // true
console.log(Days["Tue"] === 2); // true
console.log(Days["Sat"] === 6); // true
// 未手动赋值的枚举项会接着上一个枚举项递增
// 如果未手动赋值的枚举项与手动赋值的重复了,TypeScript 是不会察觉到这一点的
enum Days {Sun = 3, Mon = 1, Tue, Wed, Thu, Fri, Sat};
console.log(Days["Sun"] === 3); // true
console.log(Days["Wed"] === 3); // true
console.log(Days[3] === "Sun"); // false
console.log(Days[3] === "Wed"); // true
类
类的用法
- 使用
class
定义类,使用constructor
定义构造函数 - 通过
new
生成新实例的时候,会自动调用构造函数 - 使用
extends
关键字实现继承,子类中使用super
关键字来调用父类的构造函数和方法 - 使用
getter
和setter
可以改变属性的赋值和读取行为 - 使用
static
修饰符修饰的方法称为静态方法,它们不需要实例化,而是直接通过类来调用 ES7
中可以直接在类里面定义实例属性ES7
中可以使用static
定义一个静态属性
class Animal {
constructor(name) {
this.name = name;
}
sayHi() {
return `My name is ${this.name}`;
}
}
let a = new Animal('Jack');
console.log(a.sayHi()); // My name is Jack
// 继承
class Cat extends Animal {
constructor(name) {
super(name); // 调用父类的 constructor(name)
console.log(this.name);
}
sayHi() {
return 'Meow, ' + super.sayHi(); // 调用父类的 sayHi()
}
}
let c = new Cat('Tom'); // Tom
console.log(c.sayHi()); // Meow, My name is Tom
// getter & setter
class Animal {
constructor(name) {
this.name = name;
}
get name() {
return 'Jack';
}
set name(value) {
console.log('setter: ' + value);
}
}
let a = new Animal('Kitty'); // setter: Kitty
a.name = 'Tom'; // setter: Tom
console.log(a.name); // Jack
// 静态方法
class Animal {
static isAnimal(a) {
return a instanceof Animal;
}
}
let a = new Animal('Jack');
Animal.isAnimal(a); // true
a.isAnimal(a); // TypeError: a.isAnimal is not a function
class Animal {
name = 'Jack'; // es7中,直接在类里面定义实例属性
static age = 42; // es7中,使用 static 定义一个静态属性
constructor() {
// ...
}
}
let a = new Animal();
console.log(a.name); // Jack
console.log(Animal.age); // 42
TypeScript中类的用法
访问修饰符(
Access Modifiers
)
public
:修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是public
的private
:修饰的属性或方法是私有的,不能在声明它的类的外部访问protected
:修饰的属性或方法是受保护的,它和private
类似,区别是它在子类中也是允许被访问的
- 只读属性关键字
readonly
,只允许出现在属性声明或索引签名中
class Animal {
readonly name;
// public readonly name; // 如果 readonly 和其他访问修饰符同时存在的话,需要写在其后面
public constructor(name) {
this.name = name;
}
}
let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';
// index.ts(10,3): TS2540: Cannot assign to 'name' because it is a read-only property.
abstract
用于定义抽象类和其中的抽象方法- 抽象类是不允许被实例化的
abstract class Animal {
public name;
public constructor(name) {
this.name = name;
}
public abstract sayHi();
}
let a = new Animal('Jack');
// index.ts(9,11): error TS2511: Cannot create an instance of the abstract class 'Animal'.
- 抽象类中的抽象方法必须被子类实现
abstract class Animal {
public name;
public constructor(name) {
this.name = name;
}
public abstract sayHi();
}
class Cat extends Animal {
public eat() {
console.log(`${this.name} is eating.`);
}
// public sayHi() { // 抽象类中的抽象方法必须被子类实现
// console.log(`Meow, My name is ${this.name}`);
// }
}
let cat = new Cat('Tom'); // 子类中没有实现抽象类中的抽象方法 sayHi
// index.ts(9,7): error TS2515: Non-abstract class 'Cat' does not implement inherited abstract member 'sayHi' from class 'Animal'.
- 给类加上
Typescript
的类型,与接口类似
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()); // My name is Jack
类与接口
接口可以用于对「对象的形状(Shape
)」进行描述。
接口也可以对类的一部分行为进行抽象。
一般来讲,一个类只能继承自另一个类。
有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口。用implements
关键字来实现。
- 类实现接口
interface Alarm {
alert();
}
interface Light {
lightOn();
lightOff();
}
class Car implements Alarm, Light { // Car 实现了 Alarm 和 Light 接口
alert() {
console.log('Car alert');
}
lightOn() {
console.log('Car light on');
}
lightOff() {
console.log('Car light off');
}
}
- 接口继承接口
interface Alarm {
alert();
}
interface LightableAlarm extends Alarm { // LightableAlarm 继承自 Alarm
lightOn();
lightOff();
}
- 接口继承类
class Point {
x: number;
y: number;
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
- 一个函数可以有自己的属性和方法
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = <Counter>function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
泛型
泛型(Generics
)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法
// 在函数名后添加了 <T>
// 其中 T 用来指代任意输入的类型
// 在后面的输入 value: T 和输出 Array<T> 中即可使用了
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'); // ['x', 'x', 'x'] // 调用的时候,可以指定它具体的类型为 string
// createArray(3, 'x'); // ['x', 'x', 'x'] // 也可以不手动指定,让类型推论自动推算出来
// 多个类型参数
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
swap([7, 'seven']); // ['seven', 7]
// 泛型约束
interface Lengthwise {
length: number;
}
// 使用了 extends 约束了泛型 T 必须符合接口 Lengthwise 的形状,也就是必须包含 length 属性
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
- 泛型接口
interface CreateArrayFunc {
<T>(length: number, value: T): Array<T>;
}
let createArray: CreateArrayFunc;
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'); // ['x', 'x', 'x']
// 可以把泛型参数提前到接口名上
// 注意,此时在使用泛型接口的时候,需要定义泛型的类型
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'); // ['x', 'x', 'x']
- 泛型类
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; };
// 为泛型中的类型参数指定默认类型
function createArray<T = string>(length: number, value: T): Array<T> {
let result: T[] = [];
for (let i = 0; i < length; i++) {
result[i] = value;
}
return result;
}
声明合并
如果定义了两个相同名字的函数、接口或类,那么它们会合并成一个类型
- 函数的合并
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string {
if (typeof x === 'number') {
return Number(x.toString().split('').reverse().join(''));
} else if (typeof x === 'string') {
return x.split('').reverse().join('');
}
}
- 接口的合并(合并的属性的类型必须是唯一的)
interface Alarm {
price: number;
alert(s: string): string;
}
interface Alarm {
price: number; // 虽然重复了,但是类型都是 `number`,所以不会报错。如果不是 number,就会报错
weight: number;
alert(s: string, n: number): string;
}
// 相当于
interface Alarm {
price: number;
weight: number;
alert(s: string): string;
alert(s: string, n: number): string;
}
- 类的合并与接口的合并规则一致
代码检查
官方采用ESLint
作为代码检查的工具typescript-eslint
npm i eslint --save-dev
npm i typescript @typescript-eslint/parser --save-dev
npm i @typescript-eslint/eslint-plugin --save-dev
- 添加
.eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
rules: {
// 禁止使用 var
'no-var': "error",
// 优先使用 interface 而不是 type
'@typescript-eslint/consistent-type-definitions': [
"error",
"interface"
]
}
}
- 修改
package.json
文件:检查目录下的所有.ts
后缀的文件
{
"scripts": {
"eslint": "eslint src --ext .ts"
}
}
发表评论