TypeScript学习知识点

132 阅读12分钟

TypeScript

javaScript的超集
TypeScript = type + javaScript 在js的基础上增加了类型的支持
官方地址:www.tslang.cn/docs/handbo…

安装

npm install -g typescript

查看版本

npm view typescript version 所有可使用的版本 npm view typescript versions

查看本地是否安装

npm ls typescript 查看全局是否安装 npm ls typescript -g

卸载

npm uninstall typescript

编译命令

tsc + 文件名 即可生成对应的js文件

开发商

微软

增加类型的原因

  1. js的错误大部分都是类型报错 Uncaught TypeError js不会检查变量类型的变化Ts会
  2. TS属于静态类型编程语言(编译期间做检查) Js属于动态类型编程语言(执行期间做检查)
    所以TS可以在编译的时候就发现代码的错误,提前解决

简化执行流程

ts文件需要先编译为js文件,然后通过nodejs进行执行, 可以通过插件 ts-node 直接执行ts文件 插件 ts-node npm install -g ts-node 执行命令 ts-node + 文件名

类型注解

变量名称后用来标记变量类型的代码

常用类型

JS 原始类型 number bigInt string boolean null undefine symbol(es6新增) JS 对象类型 object array function TS 1.联合类型 2.自定义类型 3.接口 4.元祖 5.字面量 6.枚举 7.void 8.any 等 基础类型还有其他高级类型

Ts中原始类型

六种完全按照js中的写法 number string boolean null undefined symbol

let decLiteral: number = 6;
let name: string = "bob";
let isDone: boolean = false;
let sentence: string = `Hello, my name is ${ name }.`
let s:symbol = Symbol()

ts中的对象类型 每个具体的类型都有自己的类型语法

数组类型

let list: number[] = [1, 2, 3];              let list: Array<number> = [1, 2, 3];
let list: boolean[] = [true, false, true];   let list: Array<boolean> = [true, false, true];
let list: string[] = ['1','2','3']           let list: Array<string> = ['1','2','3']

Array<number>中的 number可以根据你想要使用的数组数据类型进行调整

联合类型

由一个或者多个类型组合而成的类型,对于数组而言可以解决一个数组中只能出现一种类型的问题

let arr:(number|string)[] = ["12",1]
let arr:Array<number|string> = ["12",1];

对比区分  let arr:number|string[] 表示既可以是number 类型的 也可以是string类型的数组


类型别名 也叫自定义类型

类型别名是自定义的类型,就是为任意类型起一个别名 使用场景: 定义一个较为复杂的类型并且这个类型经常会被到处使用。重复的复制同一个声明太麻烦,起一个名字直接用简短的名字表示 使用位置: 类型注解的位置 起名规则: type:标志 类型名称只要是合法变量名称即可 大写开头和小写开头都可以

// type customerArray = (number|string)[]
type customerArray = Array<number|string>;
let arrx:customerArray = ["123",1];

函数类型

主要用来控制函数的参数类型和返回类型,可以分为两类 1.单独指定参数和返回值的类型 2. 同时指定参数和返回值的类型

const 函数名称 = ()=>{
 常规函数
}
1.单独指定 的两种函数声明方式 ----传参处指定
函数声明
function add(num: number, num1: number): number {
    return num + num1
}
函数表达式
const add1 = (num: number, num1: number): number=>{
    return num + num1
}

const 函数名称 = (参数1:类型注解1,参数2,类型注解2):返回值注解 =>{
    在函数传参中写注解并在后面附返回值注解 用:连接
}

2. 同时指定参数和返回值类型 ---只适用于函数表达式 不适用于函数声明  ---------  名称处指定
const add2: (num: number, num1: number) => number = (num, num1) =>{
    return num + num1
}

const 函数名称:(参数1:类型注解1,参数2,类型注解2)=>返回值注解 = (参数1,参数1)=>{
    在名称之后接写完参数和返回值注解  参数注解和返回值注解之间用 => 连接 
}



void 类型

函数类型的一种 没有返回值的函数

const add1 = (num: number, num1: number): void => {
    console.log(1)
}

函数可选参数

函数中有的参数即可一传递又可以不传递 在这种情况下就需要可选参数

在参数后面加一个问号,并且可选参数只能出现在参数排序位置的后面
function add(num: number, num1?: number): void {
    console.log(num);
    console.log(num1);
}

对象类型

原对象
let person =  {
    name: "jack",
    age: 18,
    eat(){
        console.log("eat");
        
    }
}

TS对象

let person:{
    name: string;
    age: number;
    eat(food:string,food2:string):void
} =  {
    name: "jack",
    age: 18,
    eat(food,food2){
        console.log("eat");
        
    }
}

其中 eat(food:string,food2:string):void 可以写为  eat:(food:string,food2:string)=>void
另外 person:{
    name: string
    age: number
    eat(food:string,food2:string):void
} 
分行写的时候可以去掉; 一行的时候不能省略person:{name: string;age: number;eat(food:string,food2:string):void} 

对象的可选属性

需要加一个?类似函数可选参数

person:{
    name?: string
    age: number
    eat(food:string,food2:string):void
} 

接口类型 interface

使用场景: 当一个类型的对象经常被使用,会有重复性的代码 类似于 类型别名 和接口类型有相似的地方也有不同的地方


let person:{
    name: string;
    age: number;
    eat(food:string,food2?:string):void
} =  {
    name: "jack",
    age: 18,
    eat(food,food2){
        console.log("eat");
        console.log(food);
        console.log(food2);  
    }
}
let person1:{
    name: string;
    age: number;
    eat(food:string,food2?:string):void
} =  {
    name: "jack",
    age: 18,
    eat(food,food2){
        console.log("eat");
        console.log(food);
        console.log(food2);  
    }
}
// 此时如果需要声明一个和person相同的对象需要重复以上的代码 很繁琐
// 使用接口 
interface Person {
    name: string;
    age: number;
    eat(food:string,food2?:string):void
}

let person2:Person = {
    name: "jack",
    age: 18,
    eat(food,food2){
        console.log("eat");
        console.log(food);
        console.log(food2);  
    }
}

let person3:Person = {
    name: "jack",
    age: 18,
    eat(food,food2){
        console.log("eat");
        console.log(food);
        console.log(food2);  
    }
}

接口和类型别名的对比

相同: 都可以给对象指定类型 不同点: 使用范围不同,类型别名除了对象以外还可以为任意的类型指定别名,但是接口只能用于对象

// 使用接口 
interface Person {
    name: string;
    age: number;
    eat(food:string,food2?:string):void
}
// 使用类型别名
type Person1= {
    name: string;
    age: number;
    eat(food:string,food2?:string):void
}

let person2:Person = {
    name: "jack",
    age: 18,
    eat(food,food2){
        console.log("eat");
        console.log(food);
        console.log(food2);  
    }
}

let person3:Person1 = {
    name: "jack",
    age: 18,
    eat(food,food2){
        console.log("eat");
        console.log(food);
        console.log(food2);  
    }
}

接口之间的继承 ?? 扩展

如果两个接口之间有相同的属性或者方法,可以通过抽离公共属性方法,通过继承来实现复用 继承方式: 通过extends 关键字进行 extends 翻译为继承是否准确根据后续的用法 直接用原意扩展反而更合适

interface Point2D {
    x:number
    y:number
}

interface Point3D {
    x:number
    y:number
    z:number
}

// 使用继承来处理

interface Point2D {
    x: number
    y: number
}

interface Point3D extends Point2D { z: number }
let ponit3:Point3D = {
    x:1,
    y:2,
    z:3
}

元组类型

应用场景: 某些固定的数据比如经纬度 数组存储只需要两个长度,但是数组却可能出现任意多的元素,因此数组不严谨 元组可以明确的表示包含多少个元素和元素对应的类型

// 数组的定义
let position: Array<number> = []
let position1: number[] = []

// 元组的定义
let position3:[number,string] =[1,"23"]

position3[0] = 2;

console.log(position3)

类型推论

在ts中没有明确声明类型的地方,类型推论机制会帮助提供类型---因为类型推论的存在这些地方的注解是可以不用写的。

1. 声明时省略
// 初始化变量并指定了一个值的时候 类型推论是可以推断变量的类型 
let age = 18;
let age1: number = 18;
// age1 = "12"  会报错

let age2; 
// 这种声明了但是没有给出值, 变量 "age2" 隐式具有 "any" 类型,但可以从用法中推断出更好的类型。 类型推论给出的结果是any 类型 
age2 = 1;
age2 = "1"


2.函数的返回值省略
// 原函数声明
function add(num: number, num1: number): number {
    return num + num1
}

// 用类型推断省略 返回值注解
function add1(num: number, num1: number) {
    return num + num1
}

推荐: 能省略类型注解的地方就省略类型注解,为了开发效率方便

类型断言

使用场景: 开发人员非常确认一个值的类型,此时可以使用类型断言指定具体的值 a标签举例 const alink = document.getElementById('link'); // link.href 此时是没有值的,提示没有href属性 原因就是 getElementById 返回值是HTMLElement类型,此类型 // 只包含标签的公共的属性和方法,对于a标签的特殊属性是不包含的 因此需要更具体的类型

类型断言方法
1. as 
const alink = document.getElementById('link') as HTMLAnchorElement;
2.<> 不常用 react中也不支持 
const alink = <HTMLAnchorElement>document.getElementById('link')


技巧: 如何获取元素的类型   控制台打印 console.dir(元素) 即可获取元素类型
console.dir() 是一种在控制台中查看指定 JavaScript 对象的所有属性的方法,开发人员可以通过这种方式轻松获取对象的属性。 https://developer.mozilla.org/zh-CN/docs/Web/API/console/dir

操作:  在element中选中标签  此时该标签即为$0  使用 console.dir($0) 即可查看该元素的所有信息


字面量类型

一般配合联合类型一起使用 使用场景: 用来表示一组明确的可选值列表 这种情况也可以用枚举的方式实现

let  str1 = "hellow ts";// 类型为 strign 
const str2  = "hellow ts"; // 类型 hellow ts

// 解释: str1是一个变量 值可以是任何的字符串   
// str2是一个常量 值不能变化只能是 hellow ts  所以类型就是 hellow ts

1. 例子
一个字面量就是一个类型 
function  changeDirection(direction:"up"|"down"|"left"|"right"){
    console.log(direction)
}
changeDirection("down")
里面的四个方向每一个都是一种类型

2. 例子  通过类型别面 给联合类型命名 使结构清晰
type directionList = "up"|"down"|"left"|"right";
function  changeDirection1(direction:directionList){
    console.log(direction)
}
changeDirection1("right")

枚举类型

枚举: 定义了一组命名后的常量 访问枚举成员可以通过.来获得类似对象属性

enum direction { up, down, right, left }

function changeDirection(direction: direction) {
    console.log(direction)
}

changeDirection(direction.left)
direction.left的值为3 
枚举成员的默认的初始化值是数字值,称作数字枚举,我们也可以给枚举成员进行赋值


// 可修改枚举值
enum direction1 { up = "up", down = "down", right = "rihgt", left = 1 }
function changeDirection1(direction: direction1) {
    console.log(direction)
}

changeDirection1(direction1.down)




字符串枚举

枚举成员的值都是字符串的叫做字符串枚举 特点: 没有自增长行为,只有数字枚举才有自增长

enum direction1 { up = "up", down = "down", right = "rihgt", left = "left" }
function changeDirection1(direction: direction1) {
    console.log(direction)
}

changeDirection1(direction1.down)

枚举的特点原理

特点: 枚举不仅仅是作为类型的存在,枚举因为本身有值的存在,每个成员都有值。其他的类型会在编译为js后,自动移除,但是枚举值会被编译为js代码。

一组明确可选的的值列表,一般推荐使用 字面量类型+联合类型组合的方式实现,不采用枚举。

any类型

一般不推荐any类型,除非临时使用any,避免很长和复杂的类型

隐式具有any类型的情况:

  1. 声明变量不提供类型也不提供默认值会自动给与隐式any
  2. 函数参数不加类型的时候也会给与隐式any类型

以上的都不建议

typeof

js中的typeof:获取数据的类型 --此处需要一个区分数据类型的插件 developer.mozilla.org/zh-CN/docs/…

ts中的typeof: 在类型上下文中引用变量或属性的类型 使用场景: 根据已有变量的值,获取变量的类型,简化类型书写

let p = {
    x:1,
    y:2
}

// 1.基本写法
function format(point:{x:number;y:number}){}
format(p)
// 2. typeof 写法
function format(point: typeof p){}
format(p)

所以区别就是: 
在不同环境中typeof 具有不同的效果  (point: typeof p) 类型上下文中获得的结果和普通代码中的console.log(typeof p); 获得结果是不一样的。

即类型注解处一种结果  普通代码中一种结构

typeScript高级类型

  1. class
  2. 类型兼容性
  3. 交叉类型
  4. 泛型和keyof
  5. 索引签名类型和索引查询类型
  6. 映射类型

class

ES2015 中 class是关键字 可以声明类 在TS中依然保留,并且class声明的类可以作为一个类型存在

<!-- class 基本结构 -->
class Person {
    age: number
    gender = "男"
    // gender:string = "nan"  有初始值通过类型推断 省略了类型注解  
    
    constructor(age: number, gender: string) {
        // 类的构造函数  为实例设置初始值的目的
        this.age = age
        this.gender = gender
    }
};
const p = new Person(18,"女");

console.log(p.age)

构造函数不指定返回值类型,因为实例是person 类型。


<!-- class 实例方法 -->

class Point {
    x: number
    y: number

    constructor(x: number, y: number) {
        // 类的构造函数  为实例设置初始值的目的
        this.x = x
        this.y = y
    }

    scale(n: number): void {
        // 实例方法
        this.x *= n;
        this.y *= n;
    }
};
const p = new Point(5, 2);
p.scale(10)
console.log(p.x)

<!-- class继承 -->

// 类的继承 实现公共属性公共方法的复用
class Animal {
    move() {
        console.log("跑跑");
    }
}

class Dog extends Animal {
    name = "erha"
    bark() {
        console.log("咬人")
    }
}

const dog = new Dog()
// console.log(dog.name)

// TS中的继承 1.extends继承父类   2.implements 实现接口(ts中提供的)
//  implements 翻译为实现 指的是一个类实现一个接口的

// 接口 类实现接口的时候必须提供接口的方法

interface Singable {
    sing(): void
}

class Person implements Singable {
    sing(): void {
        console.log("xiapingugo")
    }
}

let p = new Person();
// p.sing()

<!-- 类成员的可见性 -->

// 类可见性的修饰符  用来控制类的方法属性是不是可以对外部可见  1.public共有的 2. protected受保护的  3.private私有的
class Animal {
    public move() {
        // 1. 表示此方法任何公有成员都可以访问
        console.log("跑跑");
    }
    protected move1(){
        // 2. 只能在当前类和子类中调用,但是在实例对象(不管是animal还是dog的实例对象都不行)上不能调用 
        // 比如在在子类Dog中可以通过  this.move1()调用 
        console.log("跑跑1");
        this.move2()
    }
    private move2(){
        // 2. 只在当前类中可见  不管是实例对象还是子类和子类的实例对象都不可见
        console.log("跑跑2");
    }
}

class Dog extends Animal {
    name = "erha"
    bark() {
        console.log("咬人")
        this.move1()
    }
}

const dog = new Dog()
dog.bark()

<!-- readonly 只读修饰符 -->
// readonly 只读修饰符 用来防止在构造函数之外对属性值进行修改 只能修饰属性不能修饰方法
class Animal {
    // 只读修饰符  此时 如果不加:number 则 age的类型注解类型就是 18  加了number 才是number 
    readonly age:number = 6
    constructor(age:number){
        // 只读的属性只能在constructor中进行修改
        this.age = age
    }
} 

// 接口或者{}表示的对象类型 也可以使用 readonly
interface IPerson {
   readonly name: string
}

let obj :IPerson = {
    name: '是名字啊'
}

console.log(obj.name)
// obj.name = "ce"  这种会报错


let aa :{
    readonly name: number 
} = {
    name : 18
}

// aa.name = 19; 此时也会报错




类型兼容性

// 1. 类型兼容性 // 两种类型系统 1. structural type system 结构化类型系统 2. nominal type system 标明类型系统 // TS 也是 结构化类型系统,关注的是值的结构 如果两个值具有相同的结构 则认为是同一类型

class Point {
    x:number
    y:number
}

class Point3D {
    x:number
    y:number
    z:number
}

//  成员多的可以赋值给成员少的,即少的兼容多的
const pt :Point = new Point3D()

//  1. Point 和 Point2D 是不同的类  
//  2. pt 的类型注解是Point 但是值是Point2D的实例 比较Point2D实例的值和Point的结构后是相同的 因此没有报错
//  3. 属性相同,并且类型注解相同



// 2. 接口兼容性 // 参数多的可以兼容参数少的,即参数多的可以赋值给参数少的

interface Point {
    x: number
    y: number
}
interface Point2D {
    x: number
    y: number

}

interface Point3D {
    x: number
    y: number
    z: number
}

let p: Point
let p2: Point2D = {
    x: 1,
    y:2
}
let p3: Point3D 

p = p2

//  接口和类之间也是可以兼容的
class Point3D {
    x:number
    y:number
    z:number
}

let p33 :Point2D = new Point3D()

// 3. 函数的兼容性 函数参数个数 参数类型 返回值类型

// 参数少的兼容参数多的 即参数少的可以赋值给参数多的--和接口兼容和类型联通都不一样 // 相同位置的参数类型要相同 // 返回值需要相同

type F1 = (a:number) => void 
type F2 = (a:number,b:number) => void 

let f1:F1 = (a)=>{
    console.log(a);
}
let f2:F2 = f1

交叉类型 &

// 交叉类型 类似接口的继承功能 extends 用于组合多个类型为一个类型常用于对象类型

// 对比继承的举例子
interface Point2D {
    x: number
    y: number

}

interface Point3D extends Point2D {
    z: number
}

let p3: Point3D = {
    x: 1,
    y: 2,
    z: 3
}


//  交叉类型的举例
interface Person { "name": string }
interface Concat { "phone": string }
type PersonDetail = Person & Concat;
let p: PersonDetail = {
    "name": "xx",
    "phone": "sssss"
}

// 和继承的对比
// 都可以实现对象类型的组合  但是对于同名属性,处理类型冲突的方式不同

interface A {
    fn: (value: number) => string
}
// interface B extends A {
// 此种会报错 应为类型不同
//     fn:(value:string)=> string
// }

interface B1 {
    fn: (value: string) => string
}
// 此种不会报错 最终的记过相当于 fn:(value: string|number)
type C = A & B1;

泛型

class也可以配合泛型类来使用

泛型类型举例

// 在保证安全的前提下,让函数等多种类型一起工作,从而实现复用,常用与接口,函数,class中

// 1. 创建一个函数实现传入的参数类型和返回的类型是一样的 传入参数可以是任意类型的值

// 创建泛型函数
function id<Type>(value: Type): Type {
    // 语法 : 在 函数名称后面添加一个<>,尖括号中添加类型变量可以是任意名称 此处用Type
    // 此处的Type 表示一种特殊的类型变量,他是处理类型而不是值
    // 该类型变量相当于一个类型容器,捕获用户提供的类型
    // 因为是类型 所以可以作为参数和函数的类型注解
    // console.log(value.length);    提示type上不存在属性length

    return value

    // 实现了复用的同时也保证了类型的安全
}

// 数字类型的
const num = id<number>(1);
const str = id<string>("1");

// 2. 简化泛型调用机制

// 省略<Type>  根据类型推断机制自动判定
// 尽量还是不使用,id(100) 推断出来的类型是 100  而不是number 
// 但是 num1 的类型是number
let num1 = id(100);

// 3. 泛型约束 // 默认情况下,泛型的类型变量type可以代表多个类型,会导致无法访问任何属性 // 比如id("a") 调用时就无法获取参数的长度 // 提示type上不存在属性length 此时就需要为泛型添加约束收缩类型

function id1<Type>(value: Type[]): Type[] {
    // 1. 指定更加具体的类型  
    // 将数据类型改为 type 类型的数组 就会存在length 属性
    console.log(value.length);
    return value
}


interface Ilength { length: number }
function id2<Type extends Ilength>(value: Type): Type {
    // 添加约束
    // 1.  创建描述约束的接口 Ilength 提供length 属性
    // 2. 通过 extends 关键字 使用该接口 为泛型添加约束
    // 3. 约束表示 传入的类型必须具有 length 属性
    console.log(value.length);
    return value
}
// id2(["a", "b"])

// 4. 多个泛型的情况

//  创建一个对象获取对象中的属性的值 
//  类型变量是可以出现多个的 并且变量之间也是可以约束的
function getProp<Type, Key extends keyof Type>(obj: Type, key: Key) {
    console.log(obj[key]);
    // keyof Type  keyof 关键字接收一个对象类型 生成其间名的联合类型
    // 此处获取的personx的结果 是 "name"|"age"
    //  所以key 受到了 Type 的约束
    return obj[key]
}

let personx = {
    name: "jack",
    age: 18,
}

getProp(personx, "name")

// 5. 泛型接口

// 接口后面添加 <类型变量> 接口就变成了泛型接口 
//  接口的类型变量对内部所有成员可见
//  使用泛型接口是 需要显式的指定具体类型
// 如下例子 id 返回的类型是 number  ids 返回的类型是number[]
interface IdFunc<Type> {
    id: (value: Type) => Type
    ids: () => Type[]
}

// number 是咸湿的指定泛型类型 因为接口没有类型推断的功能
let objx: IdFunc<number> = {
    id(value) { return value },
    ids() { return [1, 2, 3] }
}

// 在方法名上  ctrl + 点击  可以查看详情

// 6. 泛型类 // 在类名的后面加入<类型变量> 就可以表示为泛型类

class NumberAdd<NumType> {
    defaultValue: NumType
    add: (x: NumType, y: NumType) => NumType = (x, y) => {
        console.log(x);
        console.log(y);
        return x
    }
    constructor(value: NumType) {
        this.defaultValue = value
    }
}

const myNum = new NumberAdd<number>(5)
myNum.defaultValue = 10

myNum.add(1, 2)

// 7. 高级类型 // 泛型工具类型

// 1. partial   用来创建一个类型 将type的所有属性设置为可选
// 
interface Props {
    id: string
    children: number[]
}

type PartialDemo = Partial<Props>
![partial效果](image-1.png)

// 2.readonly 构造一个类型 所有的属性都变为只读

interface Props1 {
    id: string
    children: number[]
}

type ReadonlyDemo = Readonly<Props1>
let props1: ReadonlyDemo = {
    id: "1",
    children: []
}

// props1.id = "2" 会报错

// 3.pick 从类型中选中一组属性来构造新的类型

interface Props2 {
    id: string
    children: number[]
}

type PickDemo = Pick<Props2,"id">

// 4. Record 构造一个对象类型 属性键为keys 属性类型为type

//  两个参数 一个表示对象有哪些属性  一个表示对象的属性的类型
type RecordObj = Record<"a"|'b'|'c',string[]>
// 上面的代码类似于下面的效果
type objR111 = {
    a: string[],
    b: string[],
    c: string[],
}

// 使用
let objR:RecordObj = {
    a: ['1'],
    b: ['2'],
    c: ['3'],
}
// 以上解释  对象有三个属性分别是 a b c 属性值的类型都是 string[]

索引签名类型

// 使用场景 当对象中无法确定都有哪些属性的时候 就没有办法提前定义 此时就可以使用索引签名

interface AnyObject{
    [key:string]: number | string
}

let obj:AnyObject= {
    a:1,
    b:2,
    c: "sss"
}

//  使用[key:string] 来约束出现的属性  key是占位符可以替换成任意的变量名称 
// js对象中的键值因为都是string 类型的因此可以表示任意的属性 
//  数组中的键值是number 类型的

映射类型

type propskey = 'x' | 'y' | 'z'
type propskey1 = {
    a: number;
    b: number;
    c: boolean
}
// 原始写法
type list = {
    x: number;
    y: number;
    z: number
}

// 使用联合类型的简化
type list1 = { [key in propskey]: number }

//  使用对象类型的简化   keyof propskey1 获取对象的key值的联合类型
type list2 = { [key in keyof propskey1]: number }
// 注意 映射类型只能在自定义类型中使用  不能使用在接口中

查询类型

type propskey = {
    a: number;
    b: number;
    c: boolean
}

type a = propskey["a"]
// 查询多个
type b = propskey["a" | 'b']
type c = propskey[keyof propskey]

类型声明文件

类型声明文件 为已经存在的js库提供类型信息 然后使用这些库的时候他们也就有了类型保护机制了 xxx.d.ts就是类型声明文件

  1. 两种类型文件

.ts 包含类型信合和可执行的代码 可以被编译为js .d.ts 只包含类型信息 不会生成.js 文件 用于提供信息

  1. 类型问价的使用说明

definitelyType

比较全面的定义类型的库,基本包含所有的第三方库类型定义文件 ts 官方也提供一个类型定义查询 在tool 工具页面中 中文网站 www.typescriptlang.org/zh/tools