Typescript 基础知识总结
# 1.原始数据类型
# 1.number
const count: number = 18; // 显示注解一个number类型
# 2. string
const str: string = "小明";
# 3. boolean
const status: string = false;
# 4. null
const value: null = null;
const value: null = undefined; // 这一点null类型可以赋值undefined跟在 js中是一样的,null == undefined
2
3
# 5. undefined
const value: undefined = undefined;
const value: undefined = null; // 这一点null类型可以赋值undefined跟在 js中是一样的,null == undefined
2
3
# 6. void
JavaScript
中void
关键字的意思就是无效的,比如<a href="javascript: void(0)">
这是控制a标签的跳转默认行为TypeScript
中void
类型代表无效的,一般只用在函数上,告诉别人这个函数没有返回值。
function fn(): void {} // 正确
function testFn(): void {
return 1; // 报错,不接受返回值存在
}
function fn1(): void { return undefined} // 显示返回undefined类型,也是可以的
function fn2(): void { return null} // 显示返回null类型也可以,因为 null == undefined
2
3
4
5
6
7
8
9
# 7. never
never
一个永远不会有值的类型或者也可以说一个永远也执行不完的类型,代表用于不会有值,undefined
、null
也算做是值。一般这个类型就不会用到。
const test: never = null; // 错误
const test1: never = undefined // 错误
function Person(): never { // 正确,因为死循环了,一直执行不完
while(true) {}
}
function Person(): never { // 正确,因为递归,永远没有出口
Person()
}
function Person(): never { // 正确 代码报错了,执行不下去
throw new Error()
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 9. any和unknown
any
会跳过类型检查器对值的检查,任何值都可以赋值给any
类型,any
类型的值也可以赋值给任何类型unknown
,任何类型的值都可以赋值给它,但它只能赋值给unknown
和any
# 对象静态类型
# object && {}
const list: object = {} // 空对象
const list1: object = null; // null对象
const list: object = [] // 数组对象
const list: {} = {}
list.name = 1 // 报错 不可更改里面的字段,但是可以读取
list.toString()
2
3
4
5
6
7
8
9
# 数组
const list: [] = []; // 定义一个数组类型
const list1: number[] = [1,2] // 定义一个数组,里面值必须是number
const list2: object[] = [null, {}, []] // 定义一个数组里面必须是对象类型的
const list3: Array<number> = [1,2,3] // 泛型定义数组必须是number类型
2
3
4
5
6
7
# 类
// 类
class ClassPerson = {
name: "前端"
}
const person: ClassPerson = new Person();
person.xxx = 123; // 这行代码报错,因为当前类中不存在该xxx属性
2
3
4
5
6
7
# 函数
// 函数
const fn: () => string = () => "前端" // 定义一个变量必须是函数类型的,返回值必须是string类型
2
// 接口定义函数类型
interface SearchFunc{
(source: string, subString: string): boolean;
}
let mySearch: SearchFunc = function(source: string, subString: string) { // OK
let result = source.search(subString);
return result >-1;
};
// 函数中的 this 声明,这个this类型声明必须放在参数的首位:
interface Obj {
fn: (this: Obj, name: string) => void;
}
let obj: Obj = {
fn(name: string) {}
}
let rab: Obj ={
fn(name: string) {}
}
obj.fn("兔兔"); // OK
obj.fn.call(rab, "兔兔"); // OK
obj.fn.call(window, "兔兔"); // Error: this 应该为 Obj 类型
// 可选参数,可选参数后面不允许再出现必需参数
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + ' ' + lastName;
} else {
return firstName;
}
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
// 参数默认值
function buildName(firstName: string, lastName: string = 'Cat') {
return firstName + ' ' + lastName;
}
let tomcat = buildName('Tom', 'Cat');
let tom = buildName('Tom');
// 剩余参数
function push(array: any[], ...items: any[]) {
items.forEach(function(item) {
array.push(item);
});
}
let a = [];
push(a, 1, 2, 3);
// 重载
function reverse(x: number): number;
function reverse(x: string): string;
function reverse(x: number | string): number | string | void {
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
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# 内置对象
let s: String = new String('兔神');
let n: Number = new Number(123);
let b: Boolean = new Boolean(1);
let e: Error = new Error('Error occurred');
let d: Date = new Date();
let r: RegExp = /[a-z]/;
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
document.addEventListener('click', function(e: MouseEvent) {
// Do something
});
// 类数组对象IArguments
function sum() {
let args: IArguments = arguments;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 元组Tuple
表示一个已知数组的数量和类型的数组,定义数组中每一个值的类型
const arr: [string, number] = ["前端", 1]
const arr: [string, string] = ["前端", 1] // 报错
2
3
# 枚举Enum
enum color {
RED,
BLUE = "blue",
GREEN = "green"
}
// color["RED"] 0
// color["BLUE"] blue
// 报错
enum color {
RED,
BLUE = "blue",
GREEN
}
// good
enum color {
RED, // 0
BLUE = 4, // 4
GREEN // 5
}
enum color {
RED, // 0
BLUE = 4, // 4
GREEN // 5
}
console.log(color[4]) // BLUE
console.log(color[0]) // RED
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
# 接口Interface
定义对象类型,复用
interface Types {
name: string,
age: number
}
const testObj: Types = { name: "前端", age: 18 }
const testObj1: Types = { name: "wude", age: 18 }
2
3
4
5
6
7
8
# readonly修饰符, ?可选修饰符
interface Types {
readonly name: string, // 只读,不可更改
readonly age: number,
sex?: string // 可选属性,可以不定义
}
const testObj: Types = { name: "前端娱乐圈", age: 18}
2
3
4
5
6
7
# extends继承
interface Types {
readonly name: string,
readonly age: number,
sex?: string
}
interface ChildrenType extends Types { // 这ChildrenType接口就已经继承了父级Types接口
hobby: []
}
const testObj: ChildrenType = { name: "前端娱乐圈", age: 18, hobby: ["code", "羽毛球"] }
2
3
4
5
6
7
8
9
10
11
# propName扩展
写入不在interface里面的属性。
interface Types {
readonly name: string,
readonly age: number,
sex?: string,
[propName: string]: any // propName字段必须是 string类型 or number类型。 值是any类型,也就是任意的
}
const testObj: Types = { name: "前端", age: 19, hobby: [] }
2
3
4
5
6
7
8
# 接口中的 new
interface ClockConstructor {
new (hour: number, minute: number): any;
}
let C:ClockConstructor = class { // OK
constructor() {}
}
let C1:ClockConstructor = class { // OK
constructor(h: number) {}
}
let C2:ClockConstructor = class { // OK
constructor(h: number, m: number) {}
}
let C3:ClockConstructor = class { // Error
constructor(h: string, m: number) {}
}
let C4:ClockConstructor = class { // Error
constructor(h: number, m: number, b: number) {}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
参数少的,兼容参数多的
Ts在函数参数的比较中实际上默认采取的策略是双向协变:只有当源函数参数能够赋值给目标函数或者反过来时才能赋值成功。
# Type
类型别名就是给一种类型起个别的名字,之后只要使用这个类型的地方,都可以用这个名字作为类型代替。它只是起了一个名字,并不是创建了一个新类型。别名类型只能定义是:基础静态类型、对象静态类型、元组、联合类型。不可以定义interface
type Types = string;
type TypeUnite = string | number
const name: typeUnite = "前端娱乐圈"
const age: typeUnite = 18
2
3
4
5
6
和 interface
的区别
Types
类型别名type
不允许出现重复名字interface
接口可以出现重复类型名称,如果重复出现则是,合并起来;但是如果属性同名称,类型不一样会报错
type Types = number
type Types = string // 报错, 类型别名type不允许出现重复名字
interface Types1 {
name: string
}
interface Types1 {
age: number
}
// interface接口可以出现重复类型名称,如果重复出现则是,合并起来也就是变成 { name:string, age: number }
2
3
4
5
6
7
8
9
10
11
12
类型别名不能被
extends
和implements
,且不能出现在声明右侧的任何地方。type
实现继承,则可以使用交叉类型type A = B & C & D
。type支持表达式 interface不支持
const count: number = 123
type testType = typeof count
const count: number = 123
interface testType {
[name: typeof count]: any // 报错
}
2
3
4
5
6
7
8
- type 支持类型映射,interface不支持
type keys = "name" | "age"
type KeysObj = {
[propName in keys]: string
}
const PersonObj: KeysObj = { // 正常运行
name: "wud",
age: "18"
}
interface testType {
[propName in keys]: string // 报错
}
2
3
4
5
6
7
8
9
10
11
12
13
# 联合类型
联合类型用|
表示,说白了就是满足其中的一个类型就可以。
const statusTest: string | number = "前端娱乐圈"
const flag: boolean | number = true
2
3
但是用函数参数使用联合类型在以下情况会报错
function testStatusFn(params: number | string) {
console.log(params.toFixed()) // 报错
}
testStatusFn(1)
2
3
4
5
# typeof,in,as
// 正常
function testStatusFn(params: string | number) {
if (typeof params == "string") {
console.log(params.split)
}
if (typeof params == "number") {
console.log(params.toFixed)
}
}
testStatusFn(1)
2
3
4
5
6
7
8
9
10
11
12
interface frontEnd {
name: string
}
interface backEnd {
age: string
}
// 正常
function testStatusFn(params: frontEnd | backEnd) {
if ("name" in params) {
console.log(params.name)
}
if ("age" in params) {
console.log(params.age)
}
}
testStatusFn({name: "wude"})
// 正常
function testStatusFn(params: frontEnd | backEnd) {
if ("name" in params) {
const res = (params as frontEnd).name
console.log(res)
}
if ("age" in params) {
const res = (params as backEnd).age
console.log(res)
}
}
testStatusFn({age: 118})
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
# 交叉类型
交叉类型是将多个类型合并为一个类型。
interface A {
name: string,
age: number
}
interface B {
name: string,
gender: string
}
let a: A & B = { // OK
name: "兔兔",
age: 18,
gender: "男"
};
2
3
4
5
6
7
8
9
10
11
12
13
14
注意点:交叉类型取的多个类型的并集,但是如果key相同但是类型不同,则该key为never类型。
# 泛型
泛型是专门针对不确定的类型使用,并且灵活。泛型的使用大部分都是使用<T>
,当然也可以随便使用,如:<Test>
、<Custom>
都可以。
function test<T>(a: T, b: T) {
console.log(a, b)
}
test<number>(1, "前端") // 调用后面跟着尖括号这就是泛型的类型,这时报错,因为在调用的使用类型是number,所以只能传入相同类型的
test<boolean>(true, false)
test<string>("前端", "wude")
test<any>(1, "前端")
2
3
4
5
6
7
8
9
10
# 使用extends
进行类型约束
function test<T extends number | string, Y extends number | string>(a: T, b: Y) {
console.log(a, b)
}
test<number, string>(18, "前端")
test<string, number>("前端", 18)
2
3
4
5
6
7
# 泛型接口
interface GenericIdentityFn {
<T>(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn = identity;
2
3
4
5
6
7
可以把泛型参数提前到接口名上
interface Person<T> {
name: T;
getAge(arg: T): T;
}
let myIdentity: Person<string> = {
name: "兔兔",
getAge(name) {
return name
}
};
2
3
4
5
6
7
8
9
10
11
# 泛型类
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
2
3
4
5
6
类的静态属性不能使用泛型类型
class GenericNumber<T> {
name: T;
static zeroValue: T; // Error
add: (x: T, y: T) => T;
constructor(name: T) {
this.name = name;
}
}
2
3
4
5
6
7
8
# 泛型参数的默认类型
当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。
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
# 类
# public private protected
public
为类的公共属性,就是不管在类的内部还是外部,都可以访问该类中属性及方法。默认定义的属性及方法都是public
。private
为类的私有属性,只有在当前类里面才能访问,当前类就是{}
里面区域内。在{}
外面是不能访问private
定义的属性及方法的protected
为类的保护属性,只有在当前类和子类可以访问。也就是说用protected
属性定义的子类也可以访问。
# implements
implements
关键字只能在class
中使用,实现一个新的类,从父级或者从接口实现所有的属性和方法,如果在PersonAll
类里面不写进去接口里面已有的属性和方法则会报错
interface frontEnd {
name: string,
fn: () => void
}
class PersonAll implements frontEnd {
name: "前端";
fn() {
}
}
2
3
4
5
6
7
8
9
10
11
12
# 抽象类
抽象类使用abstract
关键字定义。abstract
抽象方法不能实例化,如果,抽象类里面方法是抽象的,那么本身的类也必须是抽象的,抽象方法不能写函数体。父类里面有抽象方法,那么子类也必须要重写该方法。
// 抽象类
abstract class Boss {
name = "秦";
call() {} // 抽象方法不能写函数体
}
class A extends Boss {
call() {
console.log(this.name);
console.log("A")
}
}
class B extends Boss {
call() {
console.log("B")
}
}
new A().call()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 工具泛型
两个关键字 keyof 和 in,
- keyof 可以用来取得一个对象接口的所有 key 值
- 而 in 则可以遍历枚举类型
interface Foo {
name: string;
age: number
}
type T = keyof Foo // -> "name" | "age"
type Keys = "a" | "b"
type Obj = {
[p in Keys]: any
} // -> { a: any, b: any }
2
3
4
5
6
7
8
9
10
# Partial
将 T 中的所有的属性都变成可选的
实现:
type Partial<T> = {
[P in keyof T]?: T[P];
};
案例:
type Animal = {
name: string,
category: string,
age: number,
eat: () => number
}
type PartOfAnimal = Partial<Animal>;
const ww: PartOfAnimal = {
eat: () => {
return 233
}
}; // 属性全部可选后,可以只赋值部分属性了
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Pick
从 T 中将所有的 K 取出来,并生成一个新的类型
源码实现:
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
interface IFoo {
name: string
age: number
height: number
}
let a:Pick<IFoo,'name'|'age'> = {
name: '张三',
age: 23
} // 正确。a包含属性name和age
let b:Pick<IFoo,'name'|'age'> = {
name: '张三',
height: 163
} // 报错。a只包含属性name和age,不包含属性height
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Record
生成一个属性为 K,属性值类型为 T 的类型
源码实现:
type Record<K extends keyof any, T> = {
[P in K]: T;
};
案例:
const obj: Record<string, string> = { 'name': '张三', 'tag': '打工人' } // 正确
const obj1: Record<'a', string> = { 'a': '453' } // 正确
const obj2: Record<'a', string> = { 'b': '453' } // 错误 obj2中没有属性a
2
3
4
5
6
7
8
9
10
11
# Required
和Partial
相反,是将 T 中的所有属性都变成必选的状态。
源码实现:
type Required<T> = {
[P in keyof T]-?: T[P];
};
interface IFoo {
a?: number
b?: number
}
const a: Required<IFoo> = {
a: 2,
b: 3
}
interface IFoo1 {
a: number
b?: number
}
const b: Required<IFoo1> = {
a: 3,
b: 3
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Readonly
将一个类型的所有成员变为只读的状态。
源码实现:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
案例
interface IFoo {
name: string
age: number
}
let a:Readonly<IFoo> = {
name: '张三',
age: 23
}
a.age = 24 // 报错,age是只读属性
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Exclude
从 T 中排除掉所有包含的 U 属性。
源码实现:
type Exclude<T, U> = T extends U ? never : T;
案例
// 相当于: type A = 'a'
type A = Exclude<'x' | 'a', 'x' | 'y' | 'z'>
let a: A = 'a' // 正确
let b: A = 'x' // 不能将类型“"x"”分配给类型“"a"”
let c: A = 'l' // 不能将类型“"l"”分配给类型“"a"”
2
3
4
5
6
7
8
9
10
# Extract
和上面的Exclude
相反。而是从 T 中提取出所有包含的 U 属性值
源码实现:
type Extract<T, U> = T extends U ? T : never;
案例
// 相当于: type A = 'x'
type A = Extract<'a' | 'x', 'x' | 'y'>
let a: A = 'x' // 正确
let b: A = 'a' // 报错:不能将类型“"a"”分配给类型“"x"”
let c: A = 'y' // 报错:不能将类型“"y"”分配给类型“"x"”
2
3
4
5
6
7
8
9
10
# NonNullable
去除 T 中包含的null或者undefined
源码实现:
type NonNullable<T> = T extends null | undefined ? never : T;
案例
type A = NonNullable<string | null | undefined>
let a: A = '李四' // 正确
let b: A = 3 // 报错:不能将类型“number”分配给类型“string”
let c: A = undefined // 报错: 不能将类型“undefined”分配给类型“string”。
2
3
4
5
6
7
8
9
# Parameters
用来获取一个函数的参数类型,而且返回的是只能包含一组类型的数组
源码实现:
type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer P) => any ? P : never;
案例
type Func = ({ user: string, age: number }) => void
type Param = Parameters<Func> // 等价与type Param = [{user: any;age: any;}]
let a: Param = [{ user: '1', age: 34 }] // 正确
let b: Param = [{ user: '1', age: 34, height: 45 }] // 报错
let c: Param = [{ user: '1', age: 34,},{ user: '1', age: 34,}] // 报错
2
3
4
5
6
7
8
9
10
11
# ConstructorParameters
获取一个类的构造函数参数类型,并以数组的形式返回
码实现:
type ConstructorParameters<T extends new (...args: any[]) => any> = T extends new (...args: infer P) => any ? P : never;
案例
class Person {
constructor(name: string, age: number) {
}
}
let a: ConstructorParameters<typeof Person> // 相当于 let a: [name: string, age: number]
a = ['张三', 34]
2
3
4
5
6
7
8
9
10
11
12
# ReturnType
用来得到一个函数的返回值类型。
源码实现:
type ReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : any;
案例
type Func = ({ user: string, age: number }) => string
type Param = ReturnType<Func> // 相当于: type Param = string
let a:Param = undefined // 报错 不能将类型“undefined”分配给类型“string”。
let b:Param = '3' // 正确
let c:Param = null // 报错 不能将类型“null”分配给类型“string”。
let d:Param = 2 // 报错 不能将类型“number”分配给类型“string”。
2
3
4
5
6
7
8
9
10
11
# InstanceType
获取一个类的实例类型,可以用获取到的实例类型来约束一个变量的赋值必须和类的成员类型完全一样才可以
源码实现:
type InstanceType<T extends new (...args: any[]) => any> = T extends new (...args: any[]) => infer R ? R : any;
案例
class Person {
name: string
age: number
getName() {
return this.name
}
}
let a: InstanceType<typeof Person> = {
name: '李四',
age: 34,
getName() {
return ''
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# Omit
用来忽略 T 中的 K 属性
源码实现:
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
案例
type Person = {
name: string
age: number
}
let a: Omit<Person, 'name'> = { age: 34 } // 正确
let b: Omit<Person, 'name'> = { height: 34 } // 报错,b只包含属性age
2
3
4
5
6
7
8
9
10
11
- 01
- 2023/07/03 00:00:00
- 02
- 2023/04/22 00:00:00
- 03
- 2023/02/16 00:00:00