TypeScript学习总结

153 阅读6分钟

本文旨在为TypeScript初学者提供系统性学习大纲及总结,内容由浅至深,持续更新

1. 基础类型

布尔值

let isDone: boolean = false;

数字

//ts和js一样,只有一种数字类型,不像其他语言中还分成了整型、浮点型等。ts所有数字都是浮点数。
let decLiteral: number = 6;

字符串

let name: string = "bob";
//支持模板字符串
let sentence: string = `Hello, my name is ${ name }`

数组

//在元素类型后面接上 `[]`,表示由此类型元素组成的一个数组
let list: number[] = [1, 2, 3];
//或者用数组泛型,`Array<元素类型>`:
let list: Array<number> = [1, 2, 3];

元祖

//一个已知元素数量和类型的数组,各元素的类型不必相同。
let x: [string, number];
x = ['hello', 10]; // OK
x = [10, 'hello']; // Error
x[1].substr(1) //Error, 'number' does not have 'substr'
//当访问一个越界的元素,会使用联合类型替代
x[3] = 'world'; // OK, 字符串可以赋值给(string | number)类型

枚举

enum Color {Red, Green, Blue} 
let c: Color = Color.Green;
//默认情况下,从`0`开始为元素编号,逐项加1,也可以手动的指定成员的数值
enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];
console.log(colorName); // 显示'Green'因为上面代码里它的值是2

Any

//为不清楚类型的变量指定一个类型
let notSure: any = 4;
//`object`类型的变量只是允许你给不清楚类型的变量赋任意值 - 但是却不能够在它上面调用任意的方法
let prettySure: Object = 4; 
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.

Void

//与`any`类型相反,它表示没有任何类型,当一个函数没有返回值时,你通常会见到其返回值类型是 `void`
function warnUser(): void {
    console.log("This is my warning message"); 
}

Null 和 Undefined

//TypeScript里,`undefined`和`null`两者各自有自己的类型分别叫做`undefined`和`null`。
//和 `void`相似,它们的本身的类型用处不是很大
let u: undefined = undefined;
let n: null = null;
//默认情况下`null`和`undefined`是所有类型的子类型

Never

//`never`类型表示的是那些永不存在的值的类型。
// `never`类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型
function error(message: string): never {
    throw new Error(message); 
}

Object

//`object`表示非原始类型,也就是除`number`,`string`,`boolean`,`symbol`,`null`或`undefined`之外的类型。
function create(o: object | null): void{
    throw new Error(message); 
};
create({ prop: 0 }); // OK 
create(null); // OK 
create(42); // Error

类型断言

//为类型并不明确的元素指定类型
let someValue: any = "this is a string"; 
let strLength: number = (<string>someValue).length;
//as语法(当你在TypeScript里使用JSX时,只有 `as`语法断言是被允许的。)
//let strLength: number = (someValue as string).length;

2. 接口

TypeScript里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

//定义一个名为LabelledValue的接口它代表一个含有label属性且label属性类型为string的对象
interface LabelledValue { 
    label: string; 
} 
function printLabel(labelledObj: LabelledValue) {//为此函数的参数设置LabelledValue接口类型
    console.log(labelledObj.label);
} 
let myObj = {
    size: 10, label: "Size 10 Object"
};
printLabel(myObj);//"Size 10 Object"

类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以

可选属性

选属性的接口在可选属性名字定义的后面加一个?符号

interface SquareConfig {
    color?: string; 
    width?: number;
} 
function createSquare(config: SquareConfig): {color: string; area: number} { 
    let newSquare = {color: "white", area: 100}; 
    if (config.color) { 
        newSquare.color = config.color; 
    } 
    if (config.width) { 
        newSquare.area = config.width * config.width;
    } 
    return newSquare; 
}
let mySquare = createSquare({color: "black"});

只读属性

该属性只能在对象刚创建时为其赋初始值,赋值后不可修改

interface Point { 
    readonly x: number; 
    readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!

ReadonlyArray<T>,确保数组创建后再也不能被修改

let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a; 
ro[0] = 12; // error! 
a = ro; // error!把整个`ReadonlyArray`赋值到一个普通数组也是不可以
//可以用类型断言重写
a = ro as number[];

额外的属性检查

如果一个传入的对象字面量存在任何interface接口不包含的属性时,你会得到一个错误。如下:

interface SquareConfig { 
    color?: string; width?: number; 
} 
function createSquare(config: SquareConfig): { color: string; area: number } { 
    // ...
} 
let mySquare = createSquare({ colour: "red", width: 100 });
//// error: 'colour' not expected in type 'SquareConfig'

如何绕开这些检查?

//方式一:使用类型断言
let mySquare = createSquare({ width: 100, opacity: 0.5 } as SquareConfig);
//方式二:加一个字符串索引签名
interface SquareConfig { 
    color?: string; 
    width?: number; 
    [propName: string]: any;
}
//方式三:将这个对象赋值给其他变量,对其他变量进行属性检查
let squareOptions = { colour: "red", width: 100 }; 
let mySquare = createSquare(squareOptions);

函数类型

接口除了描述带有属性的普通对象外,接口也可以描述函数类型

//(参数1:类型1,参数2:类型2):返回值类型
interface SearchFunc { 
    (source: string, subString: string): boolean; 
}

let mySearch: SearchFunc;
//不同于上面的对象,函数类型的函数参数名不需要与接口里定义的名字相匹配
mySearch = function(src: string, sub: string) { 
    let result = source.search(subString);
    return result > -1; 
}

如果没有指定函数参数类型,ts会根据接口自动推断出参数类型,返回值类型会根据实际返回值与接口定义的返回值作比较,例如如果让这个函数返回数字或字符串,类型检查器会报警告

let mySearch: SearchFunc;
mySearch = function(src, sub) { 
    let result = src.search(sub); 
    return result > -1; 
}

可索引的类型

定义了StringArray接口,它具有索引签名。 这个索引签名表示了当用 number去索引StringArray时会得到string类型的返回值。

interface StringArray { 
    [index: number]: string; 
} 
let myArray: StringArray; 
myArray = ["Bob", "Fred"]; 
let myStr: string = myArray[0];

同时使用两种类型的索引时,number 索引的值(value)类型必须是 string 索引值(value)类型的子类型,为什么呢?其实在JavaScript中,对象(包括数组)的索引一律为字符串,非字符串索引会被自动转为字符串,例如数组arr[0],实际最会被转换为arr['0']

下面为一个经典代码示例

interface Animal { type: string; } 
} 
interface Dog extends Animal { name: string; } 
interface DogData { 
    [x: number]: Dog;
    [x: string]: Animal; 

let hotDog: DogData = {}; 
let animal: Animal = { type: 'dog' }; 
let dog: Dog = { type: 'dog', name: 'hotDog' }; 
hotDog['0'] = animal; 
hotDog[0] = dog; // error TS2741: Property 'name' is missing in type 'Animal' but required in type 'Dog'

按说hotDog[0],索引为number,所以根据接口DogData,索引为number的那项类型为Dog接口规定的那样,既包括Dog接口本身的{name:string}也包括继承来的{type: string;}

但是,如上所说,hotDog[0]会被转换为hotDog['0'],索引为字符串,走的是Animal接口,这个接口不包括{name:string},将dog赋给hotDog[0]时会报错

类类型

使用类类型来明确的强制一个类去符合某种契约

interface ClockInterface { currentTime: Date; } 
//implements是一个类实现一个接口用的关键字
class Clock implements ClockInterface { 
    currentTime: Date; constructor(h: number, m: number) { }
}

...未完待续

4. 类

5. 函数

6. 泛型

7. 枚举类型