基础类型
TypeScript支持与JavaScript几乎相同的数据类型,包括最简单的数据单元:数字,字符串,结构体,布尔值等,定义方式如下:
// 布尔值
let isDone: boolean = false;
// 数字,支持十进制、十六进制、二进制和八进制
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let binaryLiteral: number = 0b1010;
let octalLiteral: number = 0o744;
// 字符串
let name: string = "bob";
// 数组
let list: number[] = [1, 2, 3]; // 可以在元素类型后面接上 [],表示由此类型元素组成的一个数组
let list: Array = [1, 2, 3]; // 或者使用数组泛型
// 元组
// 元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。
let x: [string, number];
// 枚举
// enum类型是对JavaScript标准数据类型的一个补充。 像C#等其它语言一样,使用枚举类型可以为一组数值赋予友好的名字。
// 默认情况下,从0开始为元素编号。 你也可以手动的指定成员的数值。
enum Color {Red, Green, Blue}
let c: Color = Color.Green;
除了这些最简单的数据单元外,TypeScript的基础类型还包括Any、Void、Object等:
- Any: 有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。 那么我们可以使用 any类型来标记这些变量:
let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // okay, definitely a boolean
- Void: 某种程度上来说,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;
- Never: never类型表示的是那些永不存在的值的类型。 例如, never类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never类型,当它们被永不为真的类型保护所约束时。
never类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是never的子类型或可以赋值给never类型(除了never本身之外)。 即使 any也不可以赋值给never。
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
throw new Error(message);
}
// 推断的返回值类型为never
function fail() {
return error("Something failed");
}
// 返回never的函数必须存在无法达到的终点
function infiniteLoop(): never {
while (true) {
}
}
- Object: object表示非原始类型,也就是除number,string,boolean,symbol,null或undefined之外的类型。
使用object类型,就可以更好的表示像Object.create这样的API。例如:
declare function create(o: object | null): void;
create({ prop: 0 }); // OK
create(null); // OK
create(42); // Error
- 类型断言: 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript会假设你,程序员,已经进行了必须的检查。
// 尖括号写法
let someValue: any = "this is a string";
let strLength: number = (someValue).length;
// as写法,jsx中只能用这个
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
接口
在TypeScript中,接口存在的意义就是对值的类型进行命名和为你的代码或第三方代码定义契约。
基础用法
interface LabelledValue {
label: string;
}
function printLabel(labelledObj: LabelledValue) {
console.log(labelledObj.label);
}
let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);
和其它语言不同的是,只要传入的对象满足接口定义的必要条件,那么就是被允许的。
可选属性
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!
在属性名前用readonly来指定只读属性。
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!
TypeScript具有ReadonlyArray
额外的属性检查
interface SquareConfig {
color?: string;
width?: number;
}
function createSquare(config: SquareConfig): { color: string; area: number } {
// ...
}
// error: 'colour' not expected in type 'SquareConfig'
let mySquare = createSquare({ colour: "red", width: 100 });
上面的代码会报错,原因是TypeScript会认为这段代码可能存在bug。对象字面量会被特殊对待而且会经过额外属性检查,当将它们赋值给变量或作为参数传递的时候。如果一个对象字面量存在任何“目标类型”不包含的属性时,就会报错。
绕开这些检查的方式有以下几种:
// 使用类型断言
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);
函数类型
interface SearchFunc {
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc;
mySearch = function(source: string, subString: string) {
let result = source.search(subString);
return result > -1;
}
接口也可以描述函数类型,如上面代码所示,它就像是一个只有参数列表和返回值类型的函数定义。参数列表里的每个参数都需要名字和类型。
可索引的类型
interface StringArray {
[index: number]: string;
}
let myArray: StringArray;
myArray = ["Bob", "Fred"];
let myStr: string = myArray[0];
TypeScript支持两种索引签名:字符串和数字。 可以同时使用两种类型的索引,但是数字索引的返回值必须是字符串索引返回值类型的子类型。
类类型
interface ClockInterface {
currentTime: Date;
setTime(d: Date);
}
class Clock implements ClockInterface {
currentTime: Date;
setTime(d: Date) {
this.currentTime = d;
}
constructor(h: number, m: number) { }
}
TypeScript中接口同样可以用来明确的强制一个类去符合某种契约。
继承接口
interface Shape {
color: string;
}
interface PenStroke {
penWidth: number;
}
interface Square extends Shape, PenStroke {
sideLength: number;
}
let square = {};
square.color = "blue";
square.sideLength = 10;
square.penWidth = 5.0;
TypeScript中的接口可以相互继承,并且一个接口可以继承多个接口
混合类型
一个对象可以同时做为函数和对象使用,并带有额外的属性。
interface Counter {
(start: number): string;
interval: number;
reset(): void;
}
function getCounter(): Counter {
let counter = function (start: number) { };
counter.interval = 123;
counter.reset = function () { };
return counter;
}
let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;
在使用JavaScript第三方库的时候,可能需要像上面那样去完整地定义类型。