TypeScript笔记
1. 基本类型
JS内置类型、全局作用域 global 上存在的对象(Window内属性对象):Array、Date、Error 等、HTMLElement、NodeList、MouseEvent 等。
2. 数组、元祖、函数类型。
元组类型允许表示一个【已知元素数量和类型】的数组,各元素的类型不必相同。(需要提前定义类型)
let tuple: [number, string] = [18, 'lin']
函数类型不返回则默认为void,可选变量放在最后且变量后加
?
即可。
函数重载则是将定义一个个叠起来,最后统一使用any实现,在函数体内判断类型。
永不返回或者抛出Error的函数可以使用never
作为返回类型// 异常 function fn(msg: string): never { throw new Error(msg) } // 死循环 千万别这么写,会内存溢出 function fn(): never { while (true) {} }
3. class、interface、implements。
- class新增了public、private、protected类型限制和抽象类型abstract。使用extends实现继承。
class Animal{ name:string constructor(name:string) { this.name = name } speak() { console.log(this.name) } } class Dog extends Animal{ voice:string constructor(name:string,voice:string) { super(name); this.voice = voice; } speak(): void { console.log(this.name,this.voice) } const dog = new Dog('xiao gou','wangwang')
interface 是 TS 设计出来用于定义对象类型的,可以对对象的形状进行描述。
interface可以对class进行约束,通过使用implements(实现)。
interface speakInterface { speak(): void } interface otherInterface { name: string arr:string[] // [propName: string]: string // 自定义属性 // 自定义属性需要注释掉否则会提示Animal错误实现接口。 } interface newInterface { new (propsName: string): void } class Animal implements speakInterface,otherInterface,newInterface{ name='aaa'; arr=['1','2','3'] speak(){ } }
4. interface与数组与鸭子类型(duck typing)
interface 写法灵活且可以支持多种自定义类型,它还有个别称叫 鸭子类型。【当看到一只鸟走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么这只鸟就可以被称为鸭子。】
只要数据满足了 interface 定义的类型,哪怕它其实一点也不是,TS 仍可以编译通过。举例1:使用interface实现一个数组结构(但是它不是真的数组,没有数组相关的方法)
interface arrInterface { [index:number]: string } const arr:arrInterface=['1','2','3'] // arr.length 报错:类型“arrInterface”上不存在属性“length”
举例2:使用interface定义函数和函数上的属性
interface funcInterface { (x:number):void name:string } const test:funcInterface = (x:number) => { } test.a = '111' // 类型“funcInterface”上不存在属性“a” test.name = '111' // 可以
5. interface与type
平时开发中,一般使用组合或者交叉类型的时候,用 type。
一般要用类的 extends 或 implements 时,用 interface。
其他情况,比如定义一个对象或者函数,就看你心情了。类型别名会给一个类型起个新名字。类型别名有时和接口很像,但是可以作用于原始值,联合类型,元组以及其它任何你需要手写的类型。
type Name = string // 基本类型 type arrItem = number | string // 联合类型 const arr: arrItem[] = [1,'2', 3] type Person = { name: Name } type Student = Person & { grade: number } // 交叉类型 type Teacher = Person & { major: string } type StudentAndTeacherList = [Student, Teacher] // 元组类型 const list:StudentAndTeacherList = [ { name: 'lin', grade: 100 }, { name: 'liu', major: 'Chinese' } ]
有相似功能却不同的两种定义数据类型的方式。
- 相同点
- 都可以定义一个对象或函数
- 都允许继承。interface 使用 extends 实现继承, type 使用交叉类型实现继承。
- interface通过extends可以继承interface / type
- type通过&继承属性,可以继承interface / type定义的类型。
// 这两种写法都可以定义函数类型 type addType = (num1:number,num2:number) => number interface addType { (num1:number,num2:number):number }
- 不同点
- interface(接口) 是 TS 设计出来用于【定义对象类型】的,可以对对象的形状进行描述。
- type 是类型别名,用于给各种类型定义别名,让 TS 写起来更简洁、清晰。
- type 可以声明基本类型、联合类型、交叉类型、元组,interface 不行
- interface可以合并重复声明,type 不行。
interface Person { name: string } interface Person { // 重复声明 interface,就合并了 age: number } const person: Person = { name: 'lin', age: 18 }
6. 泛型
举例使用泛型:在不知道可能会传入什么类型的变量前提下,为了约束函数中进、出是同一种而采用的类型占位符。
// type / interface、函数都可以使用泛型
// type Print = <T>(arg: T) => T
interface Print<T> {
(arg:T):T
}
interface Print1<T,U> {
(arg:T):T
name:U
}
const printFn:Print<string> = function Print(arg) {
console.log(arg)
return arg
}
function test<T>(param:T):T{
return param;
}
const str:string = test<string>('12121')
// class / 数组定义等也可以使用泛型
class Test<T>{
print(arg:T):T|undefined{
return arg;
}
}
const str = new Test<string>()
const arr:Array<number> = [1,2,3] // const arr: number[] = [1,2,3]
搞懂了什么是泛型之后,现在我们可以尝试使用和约束泛型。精确约束内部属性主要可以使用extends。笔记中我分为属性约束和副作用(内部函数)约束
// 属性约束
function printLength<T>(arg: T): T {
console.log(arg.length)
return arg
}
// 报错,因为不确定arg上是否有length属性,这时就可以使用interface / extends去约束
interface ILength {
length: number
}
function printLength<T extends ILength>(arg: T): T {
console.log(arg.length)
return arg
}
// 副作用约束
// 一个公共方法(不确定传入值),内部异步调用(不确定回传),如下,默认的res返回是any,无法得到类型约束
function request(url:string) {
return fetch(url).then(res => res.json())
}
// 为了更好地约束res回传值,我们可以使用泛型
interface UserInfo {
name: string
age: number
}
function request<T>(url:string): Promise<T> {
return fetch(url).then(res => res.json())
}
request<UserInfo>('user/info').then(res =>{
console.log(res)
})
索引类型 —— 我的理解中,索引类型是用于进一步简化复杂的约束操作而定义的几个方法,其中的约束仍然是针对于属性约束。
主要可以实现实现动态属性的检查 keyof(索引查询)、T[K](索引访问) 和 extends (泛型约束)。
// keyof(索引查询) 可以用于获取某种类型的所有键,其返回类型是联合类型。
interface IPerson {
name: string;
age: number;
}
type Test = keyof IPerson; // 'name' | 'age'
// T[K](索引访问) 表示接口 T 的属性 K 所代表的类型,
interface IPerson {
name: string;
age: number;
}
let type1: IPerson['name'] // string
// T extends U,表示泛型变量可以通过继承某个类型 同上文
泛型约束使用题目: 下方的变量中使用原本没有的属性却没有提示,该如何纠正?
const userInfo = {
name: 'lin',
age: '18',
}
function getValues(userInfo: any, keys: string[]) {
return keys.map(key => userInfo[key])
}
// 抽取指定属性的值
console.log(getValues(userInfo, ['name','age'])) // ['lin', '18']
// 抽取obj中没有的属性:(并没有报错)
console.log(getValues(userInfo, ['sex','outlook'])) // [undefined, undefined]
// 原本定义了一个interface userInfo,但是公用函数中最好都使用泛型占位、让函数实例填充,而不是指定具体类型
function getValues<T,K extends keyof T>(userInfo: T, keys: K[]) {
return keys.map(key => userInfo[key])
}
console.log(getValues(userInfo, ['name','age']))
console.log(getValues(userInfo, ['sex','outlook']))
7、其他简便运算符号:in、Pick、Partial、Exclude、Omit、Extract等
// in 用于遍历联合类型
type Person = "name" | "school" | "major"
type Obj = {
[p in Person]: string
}
// Partial 将类型映射成可选属性
interface IPerson {
name: string
age: number
}
type IPartial = Partial<IPerson>
let p1: IPartial = {}
8. TypeScript声明文件d.ts
如果项目是js写的,后面需要接进ts项目,那需要写一个.d.ts的声明文件来告诉外部的ts检测 我这个是什么类型,我这里已经定义了,不需要您的类型检查了。如果是ts项目那就不需要额外书写声明文件。 大部分项目的声明都放在@types/xx 这个依赖中,直接npm到项目就可以了。
declare class Test {}
以上是将HTML代码转换为Markdown格式的文本。