TS 的核心部分就是类型系统
类型注解使用 :TypeAnnotation 语法。在类型声明空间中可用的任何内容都可以用作类型注解。类型声明关键字 interface type
class类声明的空间可以当作类型注释interfacetype声明的类型不可以当作变量使用varletconst等变量声明的空间不可以当作类型注释
简单类型
原始类型
JavaScript 的基础数据类型适用于Typescript的类型系统,主要包含 number string boolean bigInt(typescript3.2) symbol(typescript4.4)
定义以使用规则如下:👇
let num: number;
num = 123;
num = 123.456;
num = '123'; // Error
let symb: symbol;
symb = Symbol(1);
symb = Symbol('abc');
特殊类型
any undefined null void never unknow
any
any 在 typescript 的类型中可以兼容各种类型,可以赋值给任意类型的数据。常用于 JavaScript 向 typescript 迁移的时候,在日常TS开发过程中考虑类型保护,应尽量减少使用 any 类型。
let power: any;
let num: number = 21;
// 赋值任意类型
power = '123';
power = 123;
power = num;
null 和 undefined
默认情况下 null 和 undefined 是所有类型的子类型。null和undefined可以给任何类型赋值。
但是开启了 strictNullChecks 的情况下 null 和 undefined 只能赋值给 void 和它们各自的类型。
void
和 any 相反,void 表示不属于任何一个类型,常用于函数中表示没有返回值。
function fun(n:number):void {
console.log(n);
}
💡 void 类型的变量只能赋值为 undefined 或 null ,没有什么意义。
never
never 类型表示永不存在的值的类型,主要用于try.catch
unknow
任何类型数据都可以赋值给 unknown 类型的变量;
unknown 类型数据只能赋值给 unknown和any类型变量。
和any不同的是👇
const n1:any = {
foo: 'abc'
};
n1.foo; // OK
const n1:unknown = {
foo: 'abc'
};
n1.foo; // ERROR: Property 'foo' does not exist on type 'unknown'.
复杂类型
数组 元祖 枚举 对象
数组 Array
第1种, 使用 [] 指定类型, 表示该数组内的元素都是这种类型:
let numbers:(number)[] = [1,2,3,4];
let numbers:(number|string)[] = [1,2,3,4,'5'];
第2种,使用范型 Array<T> 定义
let numbers:Array<number> = [1,2,3,4,5];
元祖 Tuple
元祖类型表示一个已知数据类型(顺序)和元素数量的数组。
const list1:[number, string] = [1, '2', 3]; // Error: 数量不对,元祖声明只有两个变量
const list2:[number, string] = [1, 2]; // Error: 第二个元素类型不对
const list3:[number, string] = ['1', 2]; // Error: 两个元素类型反了
const list4:[number, string] = [1, '2']; // OK
可选元素类型
在定义元组类型时,我们可以通过 ? 号来声明元组类型的可选元素。
let mytuple:[number, string, string?]
mytuple = [1, '2'];
console.log(mytuple); // [1, '2']
mytuple = [1, '2', '3'];
console.log(mytuple); // [1, '2', '3']
枚举(enum)
枚举是组织收集有关联变量的一种方式,TypeScript 支持数字的和基于字符串的枚举。其中数字枚举相对字符串枚举多了 “反向映射”。
数字枚举
enum Status {
CREATED,
INPEOCESS,
CLOSED
}
const status = Status.CREATED;
const created = Status[0];
默认情况下,CREATED 值为0,其余成员依次+1,编译之后 👇
var Status;
(function (Status) {
Status[Status["CREATED"] = 0] = "CREATED";
Status[Status["INPEOCESS"] = 1] = "INPEOCESS";
Status[Status["CLOSED"] = 2] = "CLOSED";
})(Status || (Status = {}));
var sts = Status.CREATED;
var created = Status[0];
可以设置默认值,或手动赋值。
enum Status {
CREATED = 1,
INPEOCESS,
CLOSED
}
enum Status {
CREATED = 2,
INPEOCESS = 4,
CLOSED = 6
}
字符串枚举 与数字枚举中手动赋值类似。
enum Status {
CREATED = '新建',
INPEOCESS = '开发中',
CLOSED = '已关闭'
}
编译后
var Status;
(function (Status) {
Status["CREATED"] = "新建";
Status["INPEOCESS"] = "开发中";
Status["CLOSED"] = "已关闭";
})(Status || (Status = {}));
var sts = Status.CREATED;
对象(object)
在ts中定义对象类型通常不会使用object,一般会使用接口标注具体的对象类型。
let o2:object = { a:1, b:2 };
函数(Function)
function sum(x: number, y: number): number {
return x + y
}
let sum = function(x: number, y: number): number {
return x + y
}
接口
接口(interface) 是TS的一个很重要的概念,用来抽象的描述 「对象的形状shape」。可以使用内联注释或接口的形式
// 内联注释
const person: {
name: string;
age: number;
} = {
name: '张三',
age: 18
}
// 接口形式
interface Person {
name: string;
age: number;
}
const person: Person = {
name: '张三',
age: 18
}
另外接口中可以使用readonly只读属性,限制这种类型的变量只有在初始化的时候可以赋值,后面不可以再修改。
interface Person {
name: string;
readonly age: number;
}
const person: Person = {
name: '张三',
age: 18
}
person.age ++; // Error
当接口的属性是为可选参数时,可以使用 ?: 来指定属性的类型
interface Person {
name: string;
age?: number;
}
const person: Person = {
name: '张三'
}
类型别名
顾名思义就是给类型起一个别名,使用关键字type来定义,和 interface 有很多相似之处,后面会详细介绍两者的区别
type strType = string;
type ID = number | string;
type Person = {
idCard: ID;
name: string;
readonly age: number;
}
高级类型
联合类型
A | B 表示可接受类型A或者类型B。
let numStr: number | string;
numStr = '123';
numStr = 123;
交叉类型
A & B 表示可接受的类型必须同时满足A和B两种类型。
interface IPerson {
name: string
age: number
}
interface IWorker {
work: string;
}
type worker = IPerson & IWorker;
const workerPerson: worker = {
name: '翠花',
age: 18,
work: 'coding'
}
💡👉 通常用在对象类型,如果交叉交叉两个不同的基本类型会得到一个 never 类型。type nev = string & number; 相当于 type nev = never
索引类型
使用索引类型查询操作符 keyof T 可以获取到类型T的所有public属性名构成联合类型。
class Person {
name: string
age: number
private lover: string
}
type personKeys = keyof Person
//等同于 type personKeys = "name" | "age"
其中T[K]是索引访问,获取T所有存在于K的属性的类型组成的联合类型。
function pluck<T, K extends keyof T>(obj: T, keys: K[]): T[K][] {
return keys.map(key => obj[key])
}
interface Person {
name: string
age: number
}
let person: Person = {
name: '李翠华',
age: 18
}
let strings = pluck(person, ['name', 'age'])
// strings ['李翠华', 18]
type str = Person['name' | 'age']
等同于
type str = string | number
映射类型
语法 [K in Keys] 和 for...in 类似遍历类型中的所有属性名。
type Keys = 'name' | 'hobby'
type Person = {
[K in Keys]: string
}
其中 Keys 可以是 通过 keyof T 获取到的属性联合类型,T[P]就是当前 key 的类型。
class Person {
name: string
age: number
private lover: string
}
type personKeys = keyof Person
type Optional<T> = {
[P in keyof T]?: T[P]
}
type personType = Optional<Person>
const testPerson: personType = {
name: "test",
age: 123
}
// type personType = Optional<Person> 相当于 👇
// type personType = {
// name?: string;
// age?: number;
// }
[P in keyof T]?: T[P] 全部设置为可选属性 [P in keyof T]-?: T[P] 全部设置为必须属性
范型
泛型(Generics)是允许同一个函数接受不同类型参数的一种模板。用范型可以似一个组件支持多种类型,来创建可重用的组件。
把类型当作参数传给接口、函数
function identity <T>(value: T) : T {
return value;
}
console.log(identity<Number>(1)); // 1
console.log(identity(hello)); // hello
console.log(identity<Number>([1,2,3])); // [1,2,3]
<T>类型变量可以显式设定identity(hello)也可以隐式设定,编译器自动选择类型
一些常见的变量名称
- T(Type) 表示TS类型
- K(Key) 表示对象中的键类型
- V(Value) 表示对象中的值类型
- E(Element) 表示元素类型
Exclude Extract Pick Omit
Exclude(排除)
type Exclude<T, U> = T extends U ? never : T
Exclude<T, U>从联合类型T中排除存在于联合类型U之后的联合类型。
type N = Exclude<'a' | 'b' | 'c', 'a' | 'b' | 'c'>,结果为:type N = nevertype E = Exclude<'a' | 'b' | 'd', 'b' | 'c'>,结果为:type E = 'a' | 'd'
Extract(提取)
type Extract<T, U> = T extends U ? T : never
Extract<T, U> 提取同时存在于联合类型T和联合类型U的联合类型。
type N = Extract<'a' | 'b' | 'c', 'd' | 'e'>,这次我们得到的结果是type N = nevertype E = Extract<'a' | 'b' | 'd', 'b' | 'c'>,这次我们得到的结果是type E = 'b'
Pick(筛选)
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}
Pick<T, K> 或Pick<T, K extends keyof T>从 对象类型T中筛选出存在于联合类型K中的属性,最终得到对象类型。
K extends keyof T是为了限定联合类型K必须存在于对象类型T中- 若联合类型
K中包含了对象类型T中不存在的属性,最终生成的属性类型为unknown
interface IPerson {
name: string
age: number
}
type pick = Pick<IPerson, 'name' | 'age' | 'age1'>
// 相当于
// type pick = {
// name: string;
// age: number;
// age1: unknown;
// }
Omit(过滤)
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
Omit<T, K>就是从对象类型T中将联合类型K中所有属性过滤掉,最终得到对象类型。
interface IPerson {
name: string
age: number
}
type omitT = Omit<IPerson, 'name'>
// 实际上omitT类型为 👇
// type omitT = {
// age: number;
// }
类型断言
类型断言(Type Assertion)就是手动指定一个值的类型。
as
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
尖括号
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;