# 14. TypeScript学习
- 基础
- 1. 原始数据类型包括:布尔值、数值、字符串、null、undefined 以及 ES6 中的新类型 Symbol。
- 2. 声明一个 void 类型的变量没有什么用,因为你只能将它赋值为 undefined 和 null:
- 3. undefined 和 null 是所有类型的子类型:
- 4. 类型推论
- 5. 联合类型
- 6. 接口(Interfaces)是一个很重要的概念,它是对行为的抽象:
- 7. 数组类型
- 8. 常用的类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection 等:
- 9. 函数的类型
- 10. 函数表达式
- 11. 重载
- 12. 类型断言
- 13. 声明文件
- 14. 内置对象
- 进阶
TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6 的支持,它由 Microsoft 开发。
# 基础
# 1. 原始数据类型包括:布尔值、数值、字符串、null、undefined 以及 ES6 中的新类型 Symbol。
let isDone: boolean = false;
let anyThing: any = 'hello';//任意值(未指定其类型,那么它会被识别为任意值类型)
function getLength(something: string | number): number {
return something.length;
}
2
3
4
5
6
7
# 2. 声明一个 void 类型的变量没有什么用,因为你只能将它赋值为 undefined 和 null:
function alertName(): void {
alert('My name is Tom');
}
let unusable: void = undefined;
2
3
4
5
# 3. undefined 和 null 是所有类型的子类型:
// 这样不会报错
let num: number = undefined;
2
# 4. 类型推论
let myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
// index.ts(2,1): error TS2322: Type 'number' is not assignable to type 'string'.
let myFavoriteNumber;//设为any类型
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
2
3
4
5
6
7
# 5. 联合类型
let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
2
3
# 6. 接口(Interfaces)是一个很重要的概念,它是对行为的抽象:
//不能新增也不能缺少属性
interface Person {
name: string;
age: number;
}
let tom: Person = {
name: 'Tom',
age: 25
};
//可以缺少age
interface Person {
name: string;
age?: number;
}
let tom: Person = {
name: 'Tom'
};
//任意属性
interface Person {
name: string;
age?: number;
run(): void;
[propName: string]: any;
}
let tom: Person = {
name: 'Tom',
gender: 'male'
};
//只读的约束存在于第一次给对象赋值的时候,而不是第一次给只读属性赋值的时候:
interface Person {
readonly id: number;
name: string;
age?: number;
[propName: string]: any;
}
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
# 7. 数组类型
let fibonacci: number[] = [1, 1, 2, 3, 5];
let fibonacci: Array<number> = [1, 1, 2, 3, 5];
2
3
# 8. 常用的类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection 等:
function sum() {
let args: IArguments = arguments;
}
2
3
其中 IArguments 是 TypeScript 中定义好了的类型,它实际上就是:
interface IArguments {
[index: number]: any;
length: number;
callee: Function;
}
2
3
4
5
# 9. 函数的类型
//输入多余的(或者少于要求的)参数,是不被允许的:
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.
//可选参数(可选参数必须接在必需参数后面)
function buildName(firstName: string, lastName?: string) {}
function push(array: any[], ...items: any[]) {}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 10. 函数表达式
let mySum = function (x: number, y: number): number {
return x + y;
};
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
return x + y;
};
//用接口定义函数的形状
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
return source.search(subString) !== -1;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 11. 重载
重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。
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('');
}
}
2
3
4
5
6
7
8
9
TypeScript 会优先从最前面的函数定义开始匹配,所以多个函数定义如果有包含关系,需要优先把精确的定义写在前面。
# 12. 类型断言
类型断言(Type Assertion)可以用来手动指定一个值的类型。
值 as 类型
<类型>值
(window as any).foo = 1;
interface ApiError extends Error {
code: number;
}
interface HttpError extends Error {
statusCode: number;
}
function isApiError(error: Error) {
if (typeof (error as ApiError).code === 'number') {
return true;
}
return false;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 13. 声明文件
export const name: string;
export function getName(): string;
export class Animal {
constructor(name: string);
sayHi(): string;
}
export enum Directions {
Up,
Down,
Left,
Right
}
export interface Options {
data: any;
}
// types/foo/index.d.ts
declare const name: string;
declare function getName(): string;
declare class Animal {
constructor(name: string);
sayHi(): string;
}
declare enum Directions {
Up,
Down,
Left,
Right
}
interface Options {
data: any;
}
export { name, getName, Animal, Directions, Options };
// types/foo/index.d.ts
export default function foo(): string;
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
# 14. 内置对象
Boolean、Error、Date、RegExp 等。
Document、HTMLElement、Event、NodeList
# 进阶
# 1. 类型别名
类型别名用来给一个类型起个新名字。
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
if (typeof n === 'string') {
return n;
} else {
return n();
}
}
2
3
4
5
6
7
8
9
10
# 2. 元组
let tom: [string, number] = ['Tom', 25];
# 3. 类
TypeScript 可以使用三种访问修饰符(Access Modifiers),分别是 public、private 和 protected。
public 修饰的属性或方法是公有的,可以在任何地方被访问到,默认所有的属性和方法都是 public 的 private 修饰的属性或方法是私有的,不能在声明它的类的外部访问 protected 修饰的属性或方法是受保护的,它和 private 类似,区别是它在子类中也是允许被访问的
class Animal {
public name;
// public name: string;
// readonly name;
public constructor(name) {
this.name = name;
}
}
let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';
console.log(a.name); // Tom
2
3
4
5
6
7
8
9
10
11
12
class Animal {
private name;
public constructor(name) {
this.name = name;
}
}
let a = new Animal('Jack');
console.log(a.name); // Jack
a.name = 'Tom';
// index.ts(9,13): error TS2341: Property 'name' is private and only accessible within class 'Animal'.
// index.ts(10,1): error TS2341: Property 'name' is private and only accessible within class 'Animal'.
2
3
4
5
6
7
8
9
10
11
12
13
class Animal {
protected name;
public constructor(name) {
this.name = name;
}
}
class Cat extends Animal {
constructor(name) {
super(name);
console.log(this.name);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 4. 抽象类
abstract
用于定义抽象类和其中的抽象方法。
抽象类是不允许被实例化的,抽象类中的抽象方法必须被子类实现:
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');
2
3
4
5
6
7
8
9
10
11
12
13
# 5. implements
实现(implements)是面向对象中的一个重要概念。一般来讲,一个类只能继承自另一个类,有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 implements 关键字来实现。
//类实现接口
interface Alarm {
alert(): void;
}
interface Light {
lightOn(): void;
lightOff(): void;
}
class Car implements Alarm, Light {
alert() {
console.log('Car alert');
}
lightOn() {
console.log('Car light on');
}
lightOff() {
console.log('Car light off');
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
接口继承接口
interface Alarm {
alert(): void;
}
interface LightableAlarm extends Alarm {
lightOn(): void;
lightOff(): void;
}
2
3
4
5
6
7
接口继承类
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
interface Point3d extends Point {
z: number;
}
let point3d: Point3d = {x: 1, y: 2, z: 3};
2
3
4
5
6
7
8
9
10
11
12
13
14
# 6. 泛型
泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
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']
2
3
4
5
6
7
8
9
上例中,我们在函数名后添加了 <T>
,其中 T 用来指代任意输入的类型,在后面的输入 value: T
和输出 Array<T>
中即可使用了。
function swap<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
swap([7, 'seven']); // ['seven', 7]
2
3
4
5
# 7. 泛型约束
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
2
3
4
5
6
7
8
上例中,我们使用了 extends 约束了泛型 T 必须符合接口 Lengthwise 的形状,也就是必须包含 length 属性。
# 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; };
2
3
4
5
6
7
8
# 9. 泛型参数的默认类型
在 TypeScript 2.3 以后,我们可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。
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;
}
2
3
4
5
6
7
# 10. 声明合并
如果定义了两个相同名字的函数、接口或类,那么它们会合并成一个类型:
interface Alarm {
price: number;
}
interface Alarm {
weight: number;
}
//相当于:
interface Alarm {
price: number;
weight: number;
}
interface Alarm {
price: number;
}
interface Alarm {
price: string; // 类型不一致,会报错
weight: number;
}
// index.ts(5,3): error TS2403: Subsequent variable declarations must have the same type. Variable 'price' must be of type 'number', but here has type 'string'.
interface Alarm {
price: number;
alert(s: string): string;
}
interface Alarm {
weight: number;
alert(s: string, n: number): string;
}
//相当于:
interface Alarm {
price: number;
weight: number;
alert(s: string): string;
alert(s: string, n: number): string;
}
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