在ts中,类型系统被设计为可选的,因此可以认为js就是没有类型声明的ts。
类型注解使用 :TypeAnnotation
语法
原始类型
let num: number;
let str: string;
let bool: boolean;
:number
、:string
、:boolean
都是原始类型
数组
原始类型和[]
结合,就是数组类型,如:number[]
、:string[]
、:boolean[]
let boolArray: boolean[];
boolArray = [true, false];
let strArray: string[];
strArray = ['1', '2'];
let numArray: number[];
numArray = [1,2]
内联类型
内联类型和数组类型有点像,数组是:typeAnnotation[]
,内联是:{ /* Structure */}
let name: {
first: string;
second: string;
};
name = {
first: 'John',
second: 'Doe'
};
内联类型能为我们快速提供类型注释。如果需要多次使用相同的内联注解时,可以使用下面要说的接口。
接口
接口是ts类型系统的核心,这里先做简单介绍。接口可以认为是把多个类型合并为一个新的类型。
interface name {
first: string;
second: string;
}
let name1:name = {
first: 'a',
second: 'b'
}
let name2:name = {
first: 'aa',
second: 'bb'
}
特殊类型
特殊类型包括any
、 null
、 undefined
以及 void
当我们把类型定义为any,等同于关闭了ts类型检查,因此任何类型都能赋予给他。在把js迁移到ts的时候,经常会使用到他。但过多的使用any就失去了类型检查的意义,因此应该减少使用。
let power: any;
// 赋值任意类型
power = '123';
power = 123;
除了any类型,null和undefined也可以赋值给任意类型
// "strictNullChecks": false, 该值默认为true
let num: number;
let str: string;
// 这些类型能被赋予
num = null;
str = undefined;
void类型
:void
表示函数没有返回值。
function log(message: string): void {
console.log(message);
}
泛型
在说泛型前,让我们先看一个实际的场景:假设有个函数,它接收一个列表,并且返回这个列表的反向排序。
function reverse(items) {
const toreturn = [];
for (let i = items.length - 1; i >= 0; i--) {
toreturn.push(items[i]);
}
return toreturn;
}
let reversed = reverse([1,2,3]); // [3,2,1]
let reversed1 = reverse(['1', '2', '3']); // ['3', '2', '1']
如果没有泛型,为上面的函数添加类型就比较困难(当然也可以用联合类型),因为输入的列表项可能是字符串、数字或者其他类型。使用泛型可以轻松解决。
function reverse<T>(items: T[]): T[] {
const toreturn = [];
for (let i = items.length - 1; i >= 0; i--) {
toreturn.push(items[i]);
}
return toreturn;
}
泛型用<T>
表示,T可以是任意字符。reverse函数接受类型为T的数组,数组的每一项类型为T,数组的返回值类型为T。当我们传入[1, 2, 3]
时,TypeScript 能推断出 reverse 为 number[]
类型,从而能给你类型安全。与此相似,传入['1', '2', '3']
时,TypeScript 能推断 reverse 为 string[] 类型
联合类型
联合类型用|
表示,顾名思义,他可以表示多种不同的类型。
function formatCommandline(command: string[] | string) {
let line = '';
if (typeof command === 'string') {
line = command.trim();
} else {
line = command.join(' ').trim();
}
}
上面的函数即可以接收数组也可以接收字符串。
交叉类型
交叉类型是将多个类型合并为一个类型,支持所有的类型。交叉类型用&
表示,A & B
表示同时包含 A 和 B 的结果
interface Admin {
id: number,
administrator: string,
timestamp: string
}
interface User {
id: number,
groups: number[],
createLog: (id: number) => void,
timestamp: number
}
let t: Admin & User
t!.administrator // 合法 Admin.administrator: string
t!.groups // 合法 User.groups: number[]
t!.id // 合法 id: number
t!.timestamp // 合法 timestamp: never,never表示不允许被赋值
交叉类型 Admin & User 包含了原类型的所有属性,但是要注意两个接口都拥有 id 和 timestamp 属性,且 id 类型相同,timestamp 类型不同。在此交叉类型中,timestamp 属性类型冲突,不可被赋值。
一个实际的使用场景,mixins函数
function extend<T, U>(first: T, second: U): T & U {
for(const key in second) {
(first as T & U)[key] = second[key] as any
}
return first as T & U
}
var x = extend({ a: 'hello' }, { b: 42 });
元组类型
元组类型用于约束数组中的子项,使用:[typeofmember1, typeofmember2]
表示
const nameAndNumber: [string, number];
nameAndNumber = ['wmui', 18]
类型别名
类型别名可以让我们给类型起一个别名,在联合类型和交叉类型中比较实用,使用type SomeName = someValidTypeAnnotation
来创建
type text = string | { text: string };
type coordinates = [number, number];
type callback = (data: string) => void;