TS备忘录

85 阅读16分钟

说明

工作在TS的严格模式下

基础类型

string

let str:string = 'ts'
let str1:string = `${str}1`

number

let notNumber:number = NaN // 非数字
let infinityNumber:number = Infinity // 无穷大
​
​
let binary:number = 0b1010 // 二进制
let octal: number = 0o744;//八进制
let num:number = 123 // 十进制
let hex:number = 0xf00d // 十六进制

boolean

注意通过new出来的是对象

let createdBoolean: Boolean = new Boolean(1)

Boolean != boolean

let booleand: boolean = true //可以直接使用布尔值
let booleand2: boolean = Boolean(1) //也可以通过函数返回布尔值

null

let n: null = null;//定义null

undefined

let u: undefined = undefined;//定义undefined

void

JavaScript 没有空值(Void)的概念

在 TypeScript 中,可以用 void 表示没有任何返回值的函数

function voidFn(): void {
    console.log('test void')
}

void可以接收undefined

let u: void = undefined

任意类型

any

对any类型进行赋值不进行类型检查,any类型赋值给其它变量(与值类型相同)依然要进行类型检查

声明变量的时候没有指定任意类型默认为any

any可以进行.操作

let anys;
anys = '123'
anys = true
let anys:any = 123
anys = '123'
anys = true

unknown

对unknown类型进行赋值不进行类型检查unknown只能赋值给any和unknown的变量,不管其值是什么

unknown不能进行.操作

接口

在给接口赋值时,通过花括号完成

interface

interface Person {
    b:string,
    a:string
} 
const person:Person  = {
    a:"213"
}
// 会报错默认情况下interface定义的属性必须全部包含

接口函数

TS可以抽象函数,这应该是java没有的

interface Func{
   (x:number):number // 注意,接口在抽象函数时的写法,与函数的两种类型定义方式都不一致
}
let f:Func = function(x1:number):number{
   console.log(x1)
   return 0
}

合并

重名的interface会在程序执行前合并

interface A{name:string}
let x:A={name:'xx',age:20}
interface A{age:number} // 注意,虽然看似age:number实例化后执行,但是合并先于实例化

继承

interface B extends A{
    age:number
}

可选属性

interface Person {
    b?:string,
    a:string
}
 
const person:Person  = {
    a:"213"
}

任意属性

interface Person {
    b?:string,
    a:string,
    [propName: string]: any;
}
 
const person:Person  = {
    a:"213",
    c:"123"
}

只读属性

readonly 只读属性是不允许被赋值的只能读取

//这样写是会报错的
//因为a是只读的不允许重新赋值
interface Person {
    b?: string,
    readonly a: string,
    [propName: string]: any;
}
 
const person: Person = {
    a: "213",
    c: "123"
}
 
person.a = 123

添加函数

interface Person {
    b?: string,
    readonly a: string,
    [propName: string]: any;
    cb():void
}
 
const person: Person = {
    a: "213",
    c: "123",
    cb:()=>{
        console.log(123)
    }
}

TS和类和java类差不多,不支持方法重载

class Person {
    public name:string
    private age:number 
    protected some:any
    constructor (name:string,ages:number,some:any) {
       this.name = name
       this.age = ages
       this.some = some
    }
    run () {
 
    }
}

权限修饰符

public

内部访问 也可以外部访问 如果不写默认就是public

private

只能在内部访问 不能在外部访问

protected

只能在内部和继承的子类中访问 不能在外部访问

statice

与java相同

实现接口

interface PersonClass {
    get(type: boolean): boolean
}

interface PersonClass2 {
    set(): void,
    asd: string
}

class A {
    name: string
    constructor() {
        this.name = "123"
    }
}

class Person extends A implements PersonClass, PersonClass2 {
    asd: string
    constructor() {
        super()
        this.asd = '123'
    }
    get(type: boolean) {
        return type
    }
    set() {

    }
}

继承

class Person {
    public name: string
    private age: number
    protected some: any
    constructor(name: string, ages: number, some: any) {
        this.name = name
        this.age = ages
        this.some = some
    }
    run() {

    }
}

class Man extends Person {
    constructor() {
        super("张三", 18, 1)
        console.log(this.some)
    }
    create() {
        console.log(this.some)
    }
}
let xiaoman = new Person('小满', 18, 1)
let man = new Man()
man.some

抽象类

abstract class A {
    name: string
    constructor(name: string) {
        this.name = name;
    }
    print(): string {
        return this.name
    }

    abstract getName(): string
}

class B extends A {
    constructor() {
        super('小满')
    }
    getName(): string {
        return this.name
    }
}

let b = new B();
console.log(b.getName());

数组类型

定义

括号方式

let arr:number[] = [1,2,3,4,5]

泛型方式

let arr:Array<number> = [1,2,3,4,5]

多维数组

括号方式

let arr:number[][] = [[1,2,3,4,5]]

泛型方式

let arr:Array<Array<Number>>  = [[1,2,3,4,5]]

arguments类数组

function Arr(...args:any): void {
    let arr:IArguments = arguments  // ts内置IArguments接口,用于定义参数数组变量
}
Arr(111, 222, 333)
interface IArguments {
    [index: number]: any;
    length: number;
    callee: Function;
}

元组类型

元组就是数组的变种,元组(tuple)是固定数量的不同类型的元素的组合

let arr:[number,string] = [1,'string'] // 类型数量位置必须一一对应
let arr2: readonly [number,boolean,string,undefined] = [1,true,'sring',undefined] // 类型数量位置必须一一对应

越界元素

依然元组的本意是固定数量类型不同,但因为元组是数组的变种,依然是使用数组实现的,所以在正确赋值后可以使用push添加元素,但是该元素只能是元组定义类型的联合类型

函数类型

函数不声明返回值的话执行完毕默认返回void

分类

箭头函数方式

let func:(x:number)=>void // 箭头函数方式,该方式可以同时约束返回值和参数

Function方式

let func:Function // Function方式,该方式不能约束返回值和参数

函数变量

let fn:Function // 可以将其它函数赋值给fn, 注意F时大写

函数常量

通过function标识符方式定义的函数常量,不能再将函数赋值给该标识符

function fn(){}

函数扩展

参数数量相同

const fn = (name: string, age:number): string => {
    return name + age
}
fn('张三') // 报错,参数数量必须和定义时保持一致

可选参数

const fn:Function = (name: string, age?:number): string => {
    return name + age // age的值为undefined
}
fn('张三')

默认值

const fn = (name: string = "我是默认值"): string => {
    return name
}
fn()

函数重载

定义:重载是函数名字相同,而参数不同,返回类型可以相同也可以不同

TS函数重载与java方法不同,TS函数重载指的是函数签名的重载,不能对函数体进行重载,执行的是相同的函数体

参数类型重载

实现签名函数的对应参数类型应设置为any

function fn(a:number):void // 重载签名,没有函数体,因为不能对函数体重载function fn(a:any):number{ // 因为有函数体,所以该函数是实现签名,并且要重载的参数类型为any
    console.log(456)
    return 0
}

参数数量重载

实现签名函数的对应参数修饰符应设置为可选(?)

function fn(a:number):void // 重载签名,没有函数体,因为不能对函数体重载
function fn(a?:number):number{ // 因为有函数体,所以该函数是实现签名,并且要重载的参数修饰符为可选(?)
    console.log(456)
    return 0
}

泛型

泛型函数

function Sub<T,U>(a:T,b:U):Array<T|U> {
    const params:Array<T|U> = [a,b]
    return params
}
Sub<Boolean,number>(false,1)
Sub(false,1) // 支持泛型简写

泛型接口

interface MyInter<T> {
   (arg: T): T // TS接口可以直接约束函数,注意接口抽象函数时的写法,与函数的两种类型定义方式都不一致
}
 
function fn<T>(arg: T): T {
   return arg
}
 
let result: MyInter<number> = fn
 
result(123)

泛型类

class Sub<T>{
   attr: T[] = [];
   add (a:T):T[] {
      return [a]
   }
}
 
let s = new Sub<number>()
s.attr = [1,2,3]
s.add(123)
 
let str = new Sub<string>()
str.attr = ['1','2','3']
str.add('123')

泛型约束

类型约束

interface Len {
   length:number
}
 
function getLegnth<T extends Len>(arg:T) { // 泛型T上必须拥有length属性并且类型为number
  return arg.length
}
 
getLegnth<string>('123')

值约束

function prop<T, K extends keyof T>(obj: T, key: K) { // keyof将对象的自定义属性名变换成联合类型
   return obj[key]
}
 
 
let o = { a: 1, b: 2, c: 3 }
 
prop(o, 'a') 
prop(o, 'd') //此时就会报错发现找不到

联合类型

let num_str: number | string // 表示变量可以接收number数据也可以接收string数据,应当注意的是联合类型会导致类型不确定,所以默认只能使用相同的方法和属性。
// 结合类型断言就可以使用独有的方法
num_str = '010-820'
num_str = 0

联合值

let nm:1|2|4 = 1 // |不仅有限定联合类型,还有联合值的作用

交叉类型

多种类型的集合,联合对象将具有所联合类型的所有成员

interface A{
    num:number
}
interface B{
    str:string
    
}
let t: A & B = { // 如果存在相同的属性B会覆盖A的属性,类推
    num:123,
    str:"456"
}

枚举类型

使用enum关键字定义枚举

enum Types { // 默认情况下第一个元素为0,如果第n+1号元素没有指定值,那么n+1号元素值为n号元素的值+1(如果n号元素不是数字的话,n+1号元素必须指定值)
    Red,
    Green = 4,
    BLue
}
console.log(Types.Red) // 0
console.log(Types.Green) // 4
console.log(Types.BLue)  // 5

const枚举

枚举元素值只能是number和string

let 和 var 都是不允许的声明只能使用const,const声明和普通声明它们的区别是编译后js文件的区别

const enum Types{ // const声明
    No = "No",
    Yes = 1,
 }
 console.log(Types.No)
console.log("No" /* Types.No */); // const在编译后,直接将对应的值编译到对应的位置
enum Types{ // 普通声明
    No = "No",
    Yes = 1,
 }
 console.log(Types.No)
var Types;
(function (Types) {
    Types["No"] = "No";
    Types[Types["Yes"] = 1] = "Yes";
})(Types || (Types = {}));
console.log(Types.No); // 直接通过立即执行函数编译成对象

反向映射

通过const枚举案例可知,当普通声明并且值为number时,使用键和值进行了交替使用赋值。所以满足上述条件可以通过值拿到键。

enum Types{
    No = "No",
    Yes = 1,
 }
 console.log(Types[1]) // Yes

never

never 类型用来表示不应该存在的状态,而void表示没有

type sn = string & number // sn会被推论成never类型,因为string和number交叉类型不存在
function go():never{ // 函数一直执行,所以不存在返回值
    while(true){
        
    }
}
function go():never{ // 函数报错,所以不存在返回值
    throw new Error
}

Symbol

与js相同,只有通过以下三种方法可以获取Symbol属性对应的数据

const symbol1 = Symbol('666')
const symbol2 = Symbol('777')
const obj1= {
   [symbol1]: '小满',
   [symbol2]: '二蛋',
   age: 19,
   sex: '女'
}
​
​
console.log(obj1[symbol1]) // 方法1,持有Symbol对象
console.log(obj1[Reflect.ownKeys(obj1)[3]]) // 方法2,通过Reflect.ownKeys(obj1)将定义的属性转数组,再通过方法1获取
console.log(obj1[Object.getOwnPropertySymbols(obj1)[0]]) //方法3,通过Object.getOwnPropertySymbols(obj1)将定义的--Symbol--属性转数组,再通过方法1获取

标准内置对象

DOM 和 BOM 的内置对象

document.body

let body: HTMLElement = document.body;
console.log(body)

document.querySelectorAll

let allDiv: NodeList = document.querySelectorAll('div');

document.getElementById

let node:Node | null = document.getElementById("i")
console.log(node)

dom元素的映射表

interface HTMLElementTagNameMap {
    "a": HTMLAnchorElement;
    "abbr": HTMLElement;
    "address": HTMLElement;
    "area": HTMLAreaElement;
    "article": HTMLElement;
    "aside": HTMLElement;
    "audio": HTMLAudioElement;
    "b": HTMLElement;
    "base": HTMLBaseElement;
    "bdi": HTMLElement;
    "bdo": HTMLElement;
    "blockquote": HTMLQuoteElement;
    "body": HTMLBodyElement;
    "br": HTMLBRElement;
    "button": HTMLButtonElement;
    "canvas": HTMLCanvasElement;
    "caption": HTMLTableCaptionElement;
    "cite": HTMLElement;
    "code": HTMLElement;
    "col": HTMLTableColElement;
    "colgroup": HTMLTableColElement;
    "data": HTMLDataElement;
    "datalist": HTMLDataListElement;
    "dd": HTMLElement;
    "del": HTMLModElement;
    "details": HTMLDetailsElement;
    "dfn": HTMLElement;
    "dialog": HTMLDialogElement;
    "dir": HTMLDirectoryElement;
    "div": HTMLDivElement;
    "dl": HTMLDListElement;
    "dt": HTMLElement;
    "em": HTMLElement;
    "embed": HTMLEmbedElement;
    "fieldset": HTMLFieldSetElement;
    "figcaption": HTMLElement;
    "figure": HTMLElement;
    "font": HTMLFontElement;
    "footer": HTMLElement;
    "form": HTMLFormElement;
    "frame": HTMLFrameElement;
    "frameset": HTMLFrameSetElement;
    "h1": HTMLHeadingElement;
    "h2": HTMLHeadingElement;
    "h3": HTMLHeadingElement;
    "h4": HTMLHeadingElement;
    "h5": HTMLHeadingElement;
    "h6": HTMLHeadingElement;
    "head": HTMLHeadElement;
    "header": HTMLElement;
    "hgroup": HTMLElement;
    "hr": HTMLHRElement;
    "html": HTMLHtmlElement;
    "i": HTMLElement;
    "iframe": HTMLIFrameElement;
    "img": HTMLImageElement;
    "input": HTMLInputElement;
    "ins": HTMLModElement;
    "kbd": HTMLElement;
    "label": HTMLLabelElement;
    "legend": HTMLLegendElement;
    "li": HTMLLIElement;
    "link": HTMLLinkElement;
    "main": HTMLElement;
    "map": HTMLMapElement;
    "mark": HTMLElement;
    "marquee": HTMLMarqueeElement;
    "menu": HTMLMenuElement;
    "meta": HTMLMetaElement;
    "meter": HTMLMeterElement;
    "nav": HTMLElement;
    "noscript": HTMLElement;
    "object": HTMLObjectElement;
    "ol": HTMLOListElement;
    "optgroup": HTMLOptGroupElement;
    "option": HTMLOptionElement;
    "output": HTMLOutputElement;
    "p": HTMLParagraphElement;
    "param": HTMLParamElement;
    "picture": HTMLPictureElement;
    "pre": HTMLPreElement;
    "progress": HTMLProgressElement;
    "q": HTMLQuoteElement;
    "rp": HTMLElement;
    "rt": HTMLElement;
    "ruby": HTMLElement;
    "s": HTMLElement;
    "samp": HTMLElement;
    "script": HTMLScriptElement;
    "section": HTMLElement;
    "select": HTMLSelectElement;
    "slot": HTMLSlotElement;
    "small": HTMLElement;
    "source": HTMLSourceElement;
    "span": HTMLSpanElement;
    "strong": HTMLElement;
    "style": HTMLStyleElement;
    "sub": HTMLElement;
    "summary": HTMLElement;
    "sup": HTMLElement;
    "table": HTMLTableElement;
    "tbody": HTMLTableSectionElement;
    "td": HTMLTableCellElement;
    "template": HTMLTemplateElement;
    "textarea": HTMLTextAreaElement;
    "tfoot": HTMLTableSectionElement;
    "th": HTMLTableCellElement;
    "thead": HTMLTableSectionElement;
    "time": HTMLTimeElement;
    "title": HTMLTitleElement;
    "tr": HTMLTableRowElement;
    "track": HTMLTrackElement;
    "u": HTMLElement;
    "ul": HTMLUListElement;
    "var": HTMLElement;
    "video": HTMLVideoElement;
    "wbr": HTMLElement;
}

接收Promise

let pm:Promise<number> = new Promise<number>((resolve,reject)=>{
       resolve(1)
   })

类型断言

需要注意的是,类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误

编译过程中会删除类型断言

as方式

(变量 as 类型)

let fn = function(num:string | number):void{
    console.log((num as string).length)
}
fn("123")

泛型方式

(<类型>变量)

let fn = function(num:string | number):void{
    console.log((<string>num).length)
}
fn("123")

类型推论

let str = "123" // 声明变量并赋初值,str被推论成string
let char // 声明变量未赋初值,char被推论成any

类型别名

变量

type XX = string | boolean | 1
let x:XX = 1 // 注意,类型别名不仅是类型取别名,还有限制取值的功能

函数

不约束返回值

type func = Function // Function关键字无法约束函数的返回值
let f:func = ()=>123

约束返回值

type func = ()=>string // 箭头函数可以约束函数的返回值
let f:func = function():string{
    return "123"
}

变量操作符

可选

只能用于接口属性定义和函数参数声明时使用

属性

interface Person {
    b?:string,
    a:string
}
const person:Person  = {
    a:"213"
}

参数

const fn:Function = (name: string, age?:number): string => {
    return name + age // age的值为undefined
}
fn('张三')

tsconfig.json配置文件

"compilerOptions": {
  "incremental": true, // TS编译器在第一次编译之后会生成一个存储编译信息的文件,第二次编译会在第一次的基础上进行增量编译,可以提高编译的速度
  "tsBuildInfoFile": "./buildFile", // 增量编译文件的存储位置
  "diagnostics": true, // 打印诊断信息 
  "target": "ES5", // 目标语言的版本
  "module": "CommonJS", // 生成代码的模板标准
  "outFile": "./app.js", // 将多个相互依赖的文件生成一个文件,可以用在AMD模块中,即开启时应设置"module": "AMD",
  "lib": ["DOM", "ES2015", "ScriptHost", "ES2019.Array"], // TS需要引用的库,即声明文件,es5 默认引用dom、es5、scripthost,如需要使用es的高级版本特性,通常都需要配置,如es8的数组新特性需要引入"ES2019.Array",
  "allowJS": true, // 允许编译器编译JS,JSX文件
  "checkJs": true, // 允许在JS文件中报错,通常与allowJS一起使用
  "outDir": "./dist", // 指定输出目录
  "rootDir": "./", // 指定输出文件目录(用于输出),用于控制输出目录结构
  "declaration": true, // 生成声明文件,开启后会自动生成声明文件
  "declarationDir": "./file", // 指定生成声明文件存放目录
  "emitDeclarationOnly": true, // 只生成声明文件,而不会生成js文件
  "sourceMap": true, // 生成目标文件的sourceMap文件
  "inlineSourceMap": true, // 生成目标文件的inline SourceMap,inline SourceMap会包含在生成的js文件中
  "declarationMap": true, // 为声明文件生成sourceMap
  "typeRoots": [], // 声明文件目录,默认时node_modules/@types
  "types": [], // 加载的声明文件包
  "removeComments":true, // 删除注释 
  "noEmit": true, // 不输出文件,即编译后不会生成任何js文件
  "noEmitOnError": true, // 发送错误时不输出任何文件
  "noEmitHelpers": true, // 不生成helper函数,减小体积,需要额外安装,常配合importHelpers一起使用
  "importHelpers": true, // 通过tslib引入helper函数,文件必须是模块
  "downlevelIteration": true, // 降级遍历器实现,如果目标源是es3/5,那么遍历器会有降级的实现
  "strict": true, // 开启所有严格的类型检查
  "alwaysStrict": true, // 在代码中注入'use strict'
  "noImplicitAny": true, // 不允许隐式的any类型
  "strictNullChecks": true, // 不允许把null、undefined赋值给其他类型的变量
  "strictFunctionTypes": true, // 不允许函数参数双向协变
  "strictPropertyInitialization": true, // 类的实例属性必须初始化
  "strictBindCallApply": true, // 严格的bind/call/apply检查
  "noImplicitThis": true, // 不允许this有隐式的any类型
  "noUnusedLocals": true, // 检查只声明、未使用的局部变量(只提示不报错)
  "noUnusedParameters": true, // 检查未使用的函数参数(只提示不报错)
  "noFallthroughCasesInSwitch": true, // 防止switch语句贯穿(即如果没有break语句后面不会执行)
  "noImplicitReturns": true, //每个分支都会有返回值
  "esModuleInterop": true, // 允许export=导出,由import from 导入
  "allowUmdGlobalAccess": true, // 允许在模块中全局变量的方式访问umd模块
  "moduleResolution": "node", // 模块解析策略,ts默认用node的解析策略,即相对的方式导入
  "baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
  "paths": { // 路径映射,相对于baseUrl
    // 如使用jq时不想使用默认版本,而需要手动指定版本,可进行如下配置
    "jquery": ["node_modules/jquery/dist/jquery.min.js"]
  },
  "rootDirs": ["src","out"], // 将多个目录放在一个虚拟目录下,用于运行时,即编译后引入文件的位置可能发生变化,这也设置可以虚拟src和out在同一个目录下,不用再去改变路径也不会报错
  "listEmittedFiles": true, // 打印输出文件
  "listFiles": true// 打印编译的文件(包括引用的声明文件)
}
 
// 指定一个匹配列表(属于自动指定该路径下的所有ts相关文件)
"include": [
   "src/**/*"
],
// 指定一个排除列表(include的反向操作)
 "exclude": [
   "demo.ts"
],
// 指定哪些文件使用该配置(属于手动一个个指定文件)
 "files": [
   "demo.ts"
]

namespace命名空间

TypeScript与ECMAScript 2015一样,任何包含顶级import或者export的文件都被当成一个模块。相反地,如果一个文件不带有顶级的import或者export声明,那么它的内容被视为全局可见的(仅仅是全局智能提示可见)

namespace其实是将空间名作为对象,要导出的变量名做属性,变量值做对应的值

namespace a {
    export const Time: number = 1000 // 命名空间中通过export将想要暴露的部分导出,
    export const fn = <T>(arg: T): T => {
        return arg
    }
    fn(Time)
}
 
a.Time
let nm = 0 // 注意,nm依然是全局可见,如果export namespace a那么nm将不可见,不满足条件

编译后

"use strict";
exports.__esModule = true;
exports.a = void 0;
var a;
(function (a) {
    a.Time = 1000; // 命名空间中通过export将想要暴露的部分导出,
    a.fn = function (arg) {
        return arg;
    };
    a.fn(a.Time);
})(a = exports.a || (exports.a = {}));
a.Time;
var nm = 0; // 注意,nm依然是全局可见

嵌套

namespace a {
    export namespace b { // 注意,在进行命名空间嵌套时,子命名空间也要使用export导出
        export class Vue {
            parameters: string
            constructor(parameters: string) {
                this.parameters = parameters
            }
        }
    }
}
​
let v = a.b.Vue
new v('1')

抽离

export namespace V { // 将命名空间对象通过export导出
    export const a = 1
}

简化

namespace A  {
    export namespace B {
        export const C = 1
    }
}
 
import X = A.B.C // 注意,ts-node不支持
 
console.log(X);

合并

namespace a{
    export let s = 0 // 注意命名空间名可以重复,但要导出的量,不能重复
}
namespace a{
    export let ss = 6
}

三斜线指令

三斜线指令是包含单个XML标签的单行注释。 注释的内容会做为编译器指令使用。

三斜线指令可放在包含它的文件的最顶端。 一个三斜线指令的前面只能出现单行或多行注释,这包括其它的三斜线指令。 如果它们出现在一个语句或声明之后,那么它们会被当做普通的单行注释,并且不具有特殊的涵义。

它用于声明文件间的依赖

a.ts

namespace A {
    export const fn = () => 'a'
}

b.ts

namespace A {
    export const fn2 = () => 'b'
}

index.ts

///<reference path="./a.ts" />
///<reference path="./b.ts" />
 
 
console.log(A);

declare

当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能

declare var 声明全局变量 declare function 声明全局方法 declare class 声明全局类 declare enum 声明全局枚举类型 declare namespace 声明(含有子属性的)全局对象 interface 和 type 声明全局类型 /// 三斜线指令

Partial

将属性全部转换为可选属性

type Person = {
    name:string,
    age:number
}
 
type p = Partial<Person>
​
// 得到
type p = {
    name?: string | undefined;
    age?: number | undefined;
}

Pick

选取指定属性并返回

type Person = {
    name:string,
    age:number,
    text:string
    address:string
}
 
type Ex = "text" | "age"
 
type A = Pick<Person,Ex>

Readonly

给所有属性添加readonly只读

Record

type k = "A" | "B"
type Person = {
    name:string
    age:number
}
type B = Record<k,Person> // type B拥有A和B属性,并且类型为Person

infer

在extends语句中,支持infer关键字,可以推断一个类型变量,高效的对类型进行模式匹配。但是,这个类型变量只能在true的分支中使用。类型的占位符

// 如果泛型T是()=> infer R的子集,则返回infer R获取到的类型,否则返回boolean
type Func<T> = T extends () => infer R ? R : boolean;
​
let func1: Func<number>; // boolean;
let func2: Func<''>; // boolean
let func3: Func<() => Promise<number>>; // Promise<number>