TypeScript学习笔记

TypeScript

文章目录

  • TypeScript
    • 一、TypeScript简介
      • 1. 什么是 TypeScript
      • 2 JS ,ES ,TS的关系
    • 二、TypeScript入门
      • 1.JavaScript存在的问题
      • 2. 静态类型检查
      • 3.非异常故障
      • 4.使用工具
      • 5.显式类型
      • 6.降级编译
      • 7.严格模式
    • 三、常用类型
      • 1.基元类型
      • 2.数组
      • 3.any
      • 4.变量上的类型注释
      • 5.函数
      • 6.对象
      • 7.联合类型(union)
      • 8.类型别名
      • 9.接口
      • 10.类型断言
      • 11.文字类型
      • 12.null **和** undefined
      • 13.枚举
      • 14.不太常见的原语
    • 四、类型缩小
      • 1.typeof类型守卫
      • 2.真值缩小
      • 3.等值缩小
      • 4.in 操作符缩小
      • 5. instanceof操作符缩小
      • 6.分配缩小
      • 7.控制流分析
      • 8.使用类型谓词
      • 9.never类型与穷尽性检查
    • 五、函数
      • 1.函数类型表达式
      • 2.调用签名
      • 3.构造签名
      • 4.泛型函数
      • 5.可选参数
      • 6.函数重载
      • 7.其他类型
      • 8.参数展开运算符
      • 9.参数解构
    • 六、对象
      • 1.属性修改器
      • 2.扩展类型
      • 3.交叉类型
      • 4.泛型对象类型
      • 5.数组类型
      • 6.元组类型
    • 七、类型操纵
      • 1.Keyof类型操作符
      • 2.Typeof 类型操作符
      • 3.索引访问类型
      • 4.条件类型
      • 5. 映射类型
    • 八、类
      • 1.继承
      • 2.成员的可见性
      • 3.静态成员
      • 4.类里的 static区块
      • 5.泛型类
      • 6. 参数属性
      • 7.类表达式
      • 8.抽象类和成员
      • 9.类之间的关系

一、TypeScript简介

TypeScript

1. 什么是 TypeScript

  • TypeScript 的目标是成为 JavaScript 程序的静态类型检查器——换句话说,是一个在代码运行之前运行的工具(静态)并确保程序的类型正确(类型检查)

  • TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集,而且本质上向这个语言添加了可选的静态类型和基于类的面向对象编程

  • TypeScript 是一种非常受欢迎的 JavaScript 语言扩展。它在现有的 JavaScript 语法之上加入了一层类型层,而这一层即使被删除,也丝毫不会影响运行时的原有表现。许多人认为 TypeScript “只是一个编译器”,但更好的理解其实是把 TypeScript 看作两个独立的系统:编译器(即处理语法的部分)和语言工具(即处理与编辑器集成的部分)。通过独立看待这两个系统,就可以得到能够解释我们之前所做决策的两个重要视角

2 JS ,ES ,TS的关系

  • JavaScript:是一种编程语言
  • ECMAScript:JavaScript 语言的标准,被称为ECMAScript标准
  • TypeScript: 是 JavaScript 的超集,即包含JavaScript 的所有元素,能运行JavaScript 的代码,并扩展了JavaScript 的语法。相比于JavaScript ,它还增加了静态类型、类、模块、接口和类型注解方面的功能,更易于大项目的开发

二、TypeScript入门

1.JavaScript存在的问题

  • JS无法对函数等的参数进行类型描述,JavaScript 只提供动态类型——运行代码看看会发生什么

2. 静态类型检查

  • TS具有静态类型检查功能

3.非异常故障

  • 错别字
  • 未调用的函数
  • 基本逻辑错误

4.使用工具

  • 安装TypeScript :npm i typescriptnpm i typescript -g 全局安装)

  • 编译 ts文件 :tsc test.ts 编译成功会生成相应的js文件

  • 解决TS和JS冲突 :tsc --init 生成配置文件

    //类型检查
    /* Type Checking */
      "strict": true,  //true 严格模式
    
  • 自动编译 :tsc --watch

  • 发出错误 :tsc -noEmitOnError test.ts 当有错误时不会编译成js文件

5.显式类型

let test:string  //变量名:类型

6.降级编译

//在配置文件中
/* Language and Environment */
    "target": "es2016",  //通过修改该配置,改变编译成的js文件符合的ECMAScript标准

7.严格模式

/* Type Checking */
    "strict": true,                                      
    // "noImplicitAny": true,  //true将对类型隐式推断为,当任何变量发出错误时都应用 any 类型
    // "strictNullChecks": true,  //null和undefined不能分配给任意类型

三、常用类型

1.基元类型

  • string :字符串
  • number:数字
  • boolean:布尔

2.数组

//第一种
let arr: number[] = [1, 2, 3]  //指定数组中的元素均为number类型,此语法适用于任何类型
//第二种
let arr2: Array<number> = [1, 2, 3]

3.any

  • 即任意类型(当你不希望某个特定值导致类型检查错误时,可以使用它)

4.变量上的类型注释

  • 当你使用 const , var , 或声明变量时 let ,可以选择添加类型注释来显式指定变量的类型

    let test: string = "aaa";
    
  • 在大多数情况下,TypeScript 会尝试自动推断代码中的类型

5.函数

  • 参数类型注释:声明函数时,可以在每个参数后添加类型注解,以声明函数接受的参数类型。参数类型注释位于参数名称之后

  • 返回类型注释:添加返回类型注释。返回类型注释出现在参数列表之后

  • 匿名函数:当一个函数出现在 TypeScript 可以确定它将如何被调用的地方时,该函数的参数会自动指定类型

    function test(name: string):void { 
        console.log("Hello, " + "!!");
    }
    

6.对象

  • 定义对象类型,列出其属性及其类型

  • 可选属性:指定其部分或全部属性是可选的。在属性名称后添加一个?

    // 参数的类型是对象类型 
    function printName(obj: { first: string; last?: string }) { //这里可以使用','或者';'分隔,
        // ... 
    	console.log(obj.last?.toUpperCase()); //当需要使用可选属性的属性时,可以采用xxx?.xxx的形式,自动检查其是否为undefined
    }
    // 两种传递参数都可以 
    printName({ first: "Felix" });
    printName({ first: "Felix", last: "Lu" })
    

7.联合类型(union)

string|number|...表示或的关系,即变量可以是这几种类型中的某一种

8.类型别名

//语法:type 别名 = 值
type Point = {  //对象类型别名
    x: number; 
    y: number; 
};

type ID = number | string //联合类型别名

9.接口

  • 一个接口声明是另一种方式来命名对象类型

    interface Point { 
        x: number; 
        y: number; 
    }
    
  • 类型别名和接口之间的差异

    • 类型别名和接口非常相似,在很多情况下可以相互替换

    • 关键区别在于扩展新类型的方式不同

      // 扩展接口 
      interface Animal { 
          name: string 
      }
      interface Bear extends Animal { 
          honey: boolean 
      }
      
      //通过交叉点扩展类型别名
      type Animal = {
          name: string
      }
      type Bear = Animal & {
          honsy: boolean
      }
      
      // 向现有接口添加新字段 
      interface MyWindow { 
          title: string 
      }
      interface MyWindow { 
          count: number 
      }
      const w: MyWindow = { 
          title: 'hello ts', 
          count: 100 
      }
      
      // 类型创建后不可更改
      

10.类型断言

  • 用于指定具体的类型

    const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;
    const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas");  //.tsx 文件中不推荐使用
    
  • TypeScript 只允许类型断言转换为更具体或不太具体的类型版本

11.文字类型

let x: "hello" = "hello" //相当于 const x = 'hello'

let y: "aa" | "bb" | "cc" // y只能为三者之一

let z: -1 | 0 | 1 //z只能为 -1 | 0 | 1

interface Options { width: number; }
let m: Options | 'auto'  //m只能为 Options | 'auto'

let b1: true = true
let b2: false = false

12.null undefined

  • 非空断言运算符( ! 后缀)

    function liveDangerously(x?: number | null) { 
        // 正确 
        console.log(x!.toFixed())
    }
    

13.枚举

  • 枚举是 TypeScript 添加到 JavaScript 的一项功能,它允许描述一个值,该值可能是一组可能的命名常量之一。

    enum Direction { 
        Up = 1, 
        Down, 
        Left, 
        Right, 
    }
    

14.不太常见的原语

  • 从 ES2020 开始,JavaScript 中有一个用于非常大的整数的原语 BigInt

    // 通过bigint函数创建bigint 
    const oneHundred: bigint = BigInt(100); 
    // 通过文本语法创建BigInt 
    const anotherHundred: bigint = 100n;
    
  • JavaScript 中有一个原语 Symbol() ,用于通过函数创建全局唯一引用

    const firstName = Symbol("name"); 
    const secondName = Symbol("name"); 
    if (firstName === secondName) { 
        // 这里的代码不可能执行 
    }
    

四、类型缩小

1.typeof类型守卫

  • JavaScript 支持一个 typeof 运算符,它可以提供有关我们在运行时拥有的值类型的非常基本的信息。TypeScript 期望它返回一组特定的字符串:

    • “string”
    • “number”
    • “bigint”
    • “boolean”
    • “symbol”
    • “undefined”
    • “object”
    • “function”
  • 在 TypeScript 中,检查 typeof 的返回值是一种类型保护

    function printAll(strs: string | string[] | null) { 
        if (typeof strs === "object") { 
            for (const s of strs) { 
                console.log(s); 
            } 
        } else if (typeof strs === "string") { 
            console.log(strs); 
        } else { 
            // 做点事 
        } 
    }
    

2.真值缩小

  • 0,NaN,"" (空字符串),0n ( bigint 零的版本),null,undefined所有值强制都转换为 false ,其他值被强制转化为 true

  • 可以在 Boolean 函数中运行值获得 boolean ,或使用较短的双布尔否定将值强制转换为 boolean (后者的优点是 TypeScript 推断出一个狭窄的文字布尔类型 true ,而将第一个推断为 boolean 类型)

    // 这两个结果都返回 true 
    Boolean("hello"); // type: boolean, value: true 
    !!"world"; // type: true, value: true
    

3.等值缩小

  • typescript 也使用分支语句做 === , !== , == ,和 != 等值检查,来实现类型缩小

4.in 操作符缩小

  • in 运算符 ,用于确定对象是否具有某个名称的属性

5. instanceof操作符缩小

  • instanceof 检查一个值是否是另一个值的“实例”。 x instanceof Foo 检查 x 的原型链是否含有 Foo.prototype

6.分配缩小

  • 为任何变量赋值时,TypeScript 会查看赋值的右侧并适当缩小左侧

7.控制流分析

  • 基于可达性的代码分析被称为控制流分析,TypeScript使用这种流分析来缩小类型,因为它遇到了类型守卫和赋值。当一个变量被分析时,控制流可以一次又一次地分裂和重新合并,该变量可以被观察到在每个点上有不同的类型

8.使用类型谓词

  • 定义一个函数,其返回类型是一个类型谓词

    type Fish = {
        name: string
        swim: () => void
    }
    
    type Bird = {
        name: string
        fly: () => void
    }
    
    function isFish(pet: Fish | Bird): pet is Fish { //谓词的形式是 parameterName is Type
        return (pet as Fish).swim !== undefined
    }
    
    function getSmallPet(): Fish | Bird { 
        let fish: Fish = { 
            name: 'gold fish', 
            swim: () => { } 
        }
        
        let bird: Bird = { 
            name: 'sparrow', 
            fly: () => { } 
        }
        
        return true ? bird : fish 
    }
    
    // 这里 pet 的 swim 和 fly 都可以访问了 
    let pet = getSmallPet() 
    if (isFish(pet)) { 
        pet.swim() 
    } 
    else { 
        pet.fly() 
    }
    
    const zoo: (Fish | Bird)[] = [getSmallPet(), getSmallPet(), getSmallPet()] 
    const underWater1: Fish[] = zoo.filter(isFish) 
    // 或者,等同于 
    const underWater2: Fish[] = zoo.filter(isFish) as Fish[] 
    // 对于更复杂的例子,该谓词可能需要重复使用 
    const underWatch3: Fish[] = zoo.filter((pet): pet is Fish => { 
    	if (pet.name === 'frog') { 
        	return false 
    	}
    	return isFish(pet) 
    })
    

9.never类型与穷尽性检查

  • never 类型代表一个不应该存在的状态:一个联合体的选项减少到你已经删除了所有的可能性并且什么都不剩的程度
  • never 类型可以分配给每个类型,但是,没有任何类型可以分配给never(除了never本身)
  • 可以使用缩小并依靠 never 的出现在 switch 语句中做详尽的检查

五、函数

1.函数类型表达式

  • 用函数类型表达式描述函数

    type GreetFunction = (a: string) => void
    

2.调用签名

type DescribableFunction = { //以在一个对象类型中写一个调用签名,使函数具有属性
    description: string,
    (someArg: number): boolean
}

function doSomething(fn: DescribableFunction) { 
    console.log(fn.description + " returned " + fn(6))
}

function fn1(n:number) { 
    console.log(n)
    return true 
}
fn1.description = 'balabala...'

doSomething(fn1)

3.构造签名

class Ctor { 
    s: string 
    constructor(s: string) { 
        this.s = s 
    } 
}

type SomeConstructor = {  //在调用签名前面添加 new 关键字来写一个构造签名
    new (s: string): Ctor 
}

function fn(ctor: SomeConstructor) { 
    return new ctor("hello") 
}

const f = fn(Ctor) 
console.log(f.s)

//在调用签名前面添加 new 关键字来写一个构造签名
interface CallOrConstruct { 
    new (s: string): Date; 
    (n?: number): number; 
}
    
function fn(date: CallOrConstruct) { 
    let d = new date('2021-11-20') 
    let n = date(100) 
}

4.泛型函数

  • 输入的类型与输出的类型有关,或者两个输入的类型以某种方式相关,可以考虑使用泛型函数

    function firstElement(arr: any[]) { 
        return arr[0]
    }
    
    //使用一个约束条件限制类型参数可以接受的类型 'Type extends { length: number }'
    function longest<Type extends { length: number }>(a: Type, b: Type) {  
        if (a.length >= b.length) {
            return a 
        } else { 
            return b
        } 
    }
    // longerArray 的类型是 'number[]' 
    const longerArray = longest([1, 2], [1, 2, 3])
    // longerString 是 'alice'|'bob' 的类型 
    const longerString = longest("alice", "bob")
    // 错误! 数字没有'长度'属性 
    const notOK = longest(10, 100)
    
    function combine<Type>(arr1: Type[], arr2: Type[]): Type[] { 
        return arr1.concat(arr2)
    }
    
    const arr = combine<string | number>([1, 2, 3], ["hello"]) //指定类型 ''
    
  • 编写优秀通用函数的准则

    • 类型参数下推
    • 使用更少的类型参数
    • 类型参数应出现两次

5.可选参数

  • 参数用 ? 标记为可选参数

    function f(x?: number) { ... }
    
  • 通过 xxx = xx 提供一个参数默认值

    function f(x = 10) { ... }
    

6.函数重载

function makeDate(timestamp: number): Date; //函数签名 重载签名
function makeDate(m: number, d: number, y: number): Date; //函数签名 重载签名
function makeDate(mOrTimestamp: number, d?: number, y?: number): Date {  //函数的主体 实现签名
    if (d !== undefined && y !== undefined) { 
        return new Date(y, mOrTimestamp, d); 
    } else { 
        return new Date(mOrTimestamp); 
    } 
}
const d1 = makeDate(12345678); 
const d2 = makeDate(5, 5, 5); 
//错误的!!!
const d3 = makeDate(1, 3);
//函数内This 的声明
interface User { 
    admin: boolean 
}

interface DB { 
    filterUsers(filter: (this: User) => boolean): User[]; 
}

const db:DB = { 
    filterUsers: (filter: (this: User) => boolean) => { 
    	let user1 = { admin: true }
		let user2 = { admin: false }
        return [user1, user2] 
	} 
}
//这里不能使用箭头函数
const admins = db.filterUsers(function (this: User) { 
    return this.admin; 
})

7.其他类型

  • void 表示没有返回值的函数的返回值
  • 特殊类型 object 指的是任何不是基元的值( string 、 number 、 bigint 、 boolean 、 symbol 、 null 或 undefined )。这与空对象类型 { } 不同,也与全局类型 Object 不同
  • unknown 类型代表任何值。这与 any 类型类似,但更安全,因为对未知 unknown 值做任何事情都是不合法的
  • never 类型表示永远不会被观察到的值。在一个返回类型中,这意味着函数抛出一个异常或终止程序的执行
  • 全局性的 Function 类型描述了诸如 bind 、 call 、 apply 和其他存在于JavaScript中所有函数值的属性。它还有一个特殊的属性,即 Function 类型的值总是可以被调用;

8.参数展开运算符

  • 形参展开(Rest Parameters)

    • rest 参数出现在所有其他参数之后,并使用 … 的语法

      function multiply(n: number, ...m: number[]) { 
          return m.map((x) => n * x); 
      }
      // 'a' 获得的值 [10, 20, 30, 40] 
      const a = multiply(10, 1, 2, 3, 4);
      
  • 实参展开(Rest Arguments)

    • spread 语法从数组中提供可变数量的参数

      const arr1 = [1, 2, 3]; 
      const arr2 = [4, 5, 6]; 
      arr1.push(...arr2);
      
      //TypeScript并不假定数组是不可变的
      // 推断的类型是 number[] -- "一个有零或多个数字的数组"
      // 不专指两个数字
      const args = [8, 5]; 
      const angle = Math.atan2(...args); //报错
      
      // 推断为2个长度的元组 
      const args = [8, 5] as const; 
      const angle = Math.atan2(...args);
      

9.参数解构

type ABC = { a: number; b: number; c: number }; 
function sum({ a, b, c }: ABC) {   //参数解构
    console.log(a + b + c); 
}

六、对象

1.属性修改器

  • 可选属性

    interface PaintOptions { 
        shape: Shape; 
        xPos?: number;  //可选属性
        yPos?: number;  //可选属性
    }
    
  • 只读属性

    interface SomeType { 
        readonly prop: string; //只读属性
    }
    
    function doSomething(obj: SomeType) { 
        // 可以读取 'obj.prop'. 
        console.log(`prop has the value '${obj.prop}'.`); 
        // 但不能重新设置值 
        obj.prop = "hello"; 
    }
    
    //readonly 只修饰该属性本身不能被重新写入
    interface Home { 
        readonly resident: { 
            name: string; 
            age: number 
        }; 
    }
    function visitForBirthday(home: Home) { 
        // 我们可以从'home.resident'读取和更新属性 
        console.log(`Happy birthday ${home.resident.name}!`); 
        home.resident.age++; 
    }
    
    function evict(home: Home) { 
        // 但是我们不能写到'home'上的'resident'属性本身
        home.resident = { 
            name: "Victor the Evictor", 
            age: 42, 
        }
    }
    
    //TypeScript在检查两个类型的属性是否兼容时,并不考虑这些类型的属性是否是 readonly ,所以 readony 属性也可以通过别名来改变
    interface Person { 
        name: string; 
        age: number; 
    }
    
    interface ReadonlyPerson { 
        readonly name: string; 
        readonly age: number; 
    }
    
    let writablePerson: Person = { 
        name: "Person McPersonface", 
        age: 42, 
    };
    // 正常工作 
    let readonlyPerson: ReadonlyPerson = writablePerson; 
    console.log(readonlyPerson.age); // 打印 '42' 
    writablePerson.age++; 
    console.log(readonlyPerson.age); // 打印 '43'
    
  • 索引签名

    interface StringArray { 
        [index: number]: string;  //索引签名 索引签名类型为number ,值类型为 string
    }
    const myArray: StringArray = ['a', 'b']; 
    const secondItem = myArray[1];
    
    //索引签名的属性类型必须是 string 或 number
    interface NumberOrStringDictionary { 
        [index: string]: number | string; 
        length: number; // 正确, length 是 number 类型 
        name: string; // 正确, name 是 string 类型 
    }
    
    //只读索引签名
    interface ReadonlyStringArray { 
        readonly [index: number]: string; 
    }
    let myArray: ReadonlyStringArray = getReadOnlyStringArray(); 
    myArray[2] = "Mallory";  //错误的
    

2.扩展类型

interface BasicAddress { 
    city: string; 
    country: string; 
} 

interface AddressWithUnit extends BasicAddress {  //扩展类型
    unit: string; 
}

3.交叉类型

  • 交叉类型,主要用于组合现有的对象类型

    interface Colorful { 
        color: string; 
    }
    
    interface Circle { 
        radius: number; 
    }
    
    type ColorfulCircle = Colorful & Circle; //交叉类型
    const cc: ColorfulCircle = { 
        color: "red", 
        radius: 42
    }
    

4.泛型对象类型

interface Box<Type> { 
    contents: Type; 
}

let box: Box<string>;

//通过泛型编写其他类型的通用辅助类型
type OrNull<Type> = Type | null; 
type OneOrMany<Type> = Type | Type[]; 
type OneOrManyOrNull<Type> = OrNull<OneOrMany<Type>>; 
type OneOrManyOrNullStrings = OneOrManyOrNull<string>;

5.数组类型

  • number[] 或 string[]实际上只是 Array 和 Array 的缩写
  • 只读数组类型:ReadonlyArray 是一个特殊的类型,描述了不应该被改变的数组

6.元组类型

  • Tuple 类型是另一种 Array 类型

    type StringNumberPair = [string, number]  //StringNumberPair 描述了其索引 0 包含字符串 和索引 1 包含数字 的数组
    
    //为特定的索引声明属性,并且用数字字面类型声明长度
    interface StringNumberPair { 
        // 专有属性 
        length: 2; 
        0: string; 
    	1: number; 
    	// 其他 'Array' 成员... 
    	slice(start?: number, end?: number): Array<string | number>; 
    }
    
    //可选的元组,元素只能出现在末尾,而且还影响到长度的类型
    type Either2dOr3d = [number, number, number?];
    
    type StringNumberBooleans = [string, number, ...boolean[]]; //其前两个元素分别是字符串和数字,但后面可以有任意数量的布尔
    type StringBooleansNumber = [string, ...boolean[], number]; //其第一个元素是字符串,然后是任意数量的布尔运算,最后是一个数字
    type BooleansStringNumber = [...boolean[], string, number]; //其起始元素是任意数量的布尔运算,最后是一个字符串,然后是一个数字
    
    //只读元组类型
    function doSomething(pair: readonly [string, number]) { ... }
    

七、类型操纵

1.Keyof类型操作符

  • keyof 运算符接收一个对象类型,并产生其键的字符串或数字字面联合

    type Point = { x: number; y: number }; 
    type P = keyof Point; 
    const p1:P = 'x' 
    const p2:P = 'y'
    
    type Mapish = { [k: string]: boolean }; 
    type M = keyof Mapish; 
    const m:M = 'a' 
    const m2:M = 10
    

2.Typeof 类型操作符

//typeof 操作符引用一个变量或属性的类型
let s = "hello"; 
let n: typeof s; 
n = 'world';

//预定义的类型 ReturnType 接收一个函数类型 并产生其 返回类型
type Predicate = (x: unknown) => boolean; 
type K = ReturnType<Predicate>;

//使用typeof去修饰函数调用是不合法的!!!
// 我们认为使用 = ReturnType 
let shouldContinue: typeof msgbox("Are you sure you want to continue?");

3.索引访问类型

const MyArray = [ 
    { name: "Alice", age: 15 }, 
    { name: "Bob", age: 23 }, 
    { name: "Eve", age: 38 }, 
];

/* type Person = { name: string; age: number; } */ 
type Person = typeof MyArray[number]; 
const p:Person = { name: 'xiaoqian', age: 11 }

// type Age = number 
type Age = typeof MyArray[number]["age"]; 
const age:Age = 11 

// 或者 
// type Age2 = number 
type Age2 = Person["age"]; 
const age2:Age2 = 11

4.条件类型

  • SomeType extends OtherType ? TrueType : FalseType;

    interface IdLabel { 
        id: number /* 一些字段 */; 
    }
    
    interface NameLabel { 
        name: string /* 另一些字段 */; 
    }
    
    type NameOrId<T extends number | string> = T extends number ? IdLabel : NameLabel; 
    function createLabel<T extends number | string>(idOrName: T): NameOrId<T> { 
        throw "unimplemented"; 
    }
    // let a: NameLabel 
    let a = createLabel("typescript"); 
    // let b: IdLabel 
    let b = createLabel(2.8); 
    // let c: NameLabel | IdLabel 
    let c = createLabel(Math.random() ? "hello" : 42);
    
  • 条件类型约束

    type MessageOf<T> = T extends { message: unknown } ? T["message"] : never; 
    interface Email { message: string; }
    interface Dog { bark(): void; }
    
    // type EmailMessageContents = string 
    type EmailMessageContents = MessageOf<Email>; 
    const emc:EmailMessageContents = 'balabala...' 
    
    // type DogMessageContents = never 
    type DogMessageContents = MessageOf<Dog>; 
    const dmc:DogMessageContents = 'error' as never;
    
  • 使用 infer 关键字编写辅助类型别名。

    //从函数类型中提取出返回类型
    type GetReturnType<Type> = Type extends (...args: never[]) => infer Return ? Return : never; 
    
    // type Num = number 
    type Num = GetReturnType<() => number>; 
    
    // type Str = string 
    type Str = GetReturnType<(x: string) => string>; 
    
    // type Bools = boolean[] 
    type Bools = GetReturnType<(a: boolean, b: boolean) => boolean[]>;
    
    // 给泛型传入 string 类型,条件类型会返回 never 
    type Never = GetReturnType<string> 
    const nev:Never = 'error' as never
    
  • 分布式条件类型

    type ToArray<Type> = Type extends any ? Type[] : never; 
    // type StrArrOrNumArr = string[] | number[] 
    type StrArrOrNumArr = ToArray<string | number>;
    
    type ToArrayNonDist<Type> = [Type] extends [any] ? Type[] : never; 
    // 'StrArrOrNumArr'不再是一个联合类型 
    // type StrArrOrNumArr = (string | number)[] 
    type StrArrOrNumArr = ToArrayNonDist<string | number>;
    

5. 映射类型

  • 映射类型是一种通用类型,它使用 PropertyKeys 的联合(经常通过 keyof 创建)迭代键来创建一个类型。

    type OptionsFlags<Type> = { 
        [Property in keyof Type]: boolean; 
    };
    //OptionsFlags 将从 Type 类型中获取所有属性,并将它们的值改为布尔值
    
    type FeatureFlags = { 
        darkMode: () => void; 
        newUserProfile: () => void; 
    };
    /*
    	type FeatureOptions = { 
    		darkMode: boolean; 
    		newUserProfile: boolean; 
    	}
    */
    type FeatureOptions = OptionsFlags<FeatureFlags>;
    
  • 在映射过程中,有两个额外的修饰符可以应用: readonly 和 ? ,它们分别影响可变性和可选性。可以通过用 - 或 + 作为前缀来删除或添加这些修饰语。默认 +

    type CreateMutable<Type> = { 
        // 从一个类型的属性中删除 "readonly"属性 
        -readonly [Property in keyof Type]: Type[Property]; 
    };
    
    type LockedAccount = { 
        readonly id: string; 
        readonly name: string; 
    };
    
    /*
    	type UnlockedAccount = { 
    		id: string; 
    		name: string;
    	}
    */
    type UnlockedAccount = CreateMutable<LockedAccount>;
    

八、类

1.继承

  • implements :实现接口 class C implements A, B {}
    • 接口中的非可选属性必须全部实现
    • 实现时只需要兼容接口中定义的类型即可,不需要与接口定义的类型完全一致
  • extends:继承 class C extends A,B{}
  • 重写方法
    • 子类重写父类的方法要兼容父类方法涉及的类型
  • 初始化顺序
  • 父类的字段被初始化
  • 父类构造函数运行
  • 子类的字段被初始化
  • 子类构造函数运行

2.成员的可见性

  • public (默认):任何对象在任何地方都可以访问
  • protected:当前类或者子类中可访问
  • private:只有当前类可访问

3.静态成员

  • static

  • 类可以有静态成员。这些成员并不与类的特定实例相关联。它们可以通过类的构造函数对象本身来访问。

  • 静态成员也可以使用相同的 public 、 protected 和 private 可见性修饰符。

  • 静态成员也会被继承。

  • name 、 length 和 call 这样的函数属性,定义为静态成员是无效的。

4.类里的 static区块

class Foo { 
    static #count = 0;  // #count是私有的
    get count() { 
        return Foo.#count; 
    }
	static {   //static区块 
        try { 
            const lastInstances = { length: 100 };
            Foo.#count += lastInstances.length; 
        }
        catch {} 
    } 
}

5.泛型类

class Box<Type> { 
    contents: Type; 
    constructor(value: Type) { 
        this.contents = value; 
    } 
}
// const b: Box 
const b = new Box("hello!");

6. 参数属性

  • TypeScript提供了特殊的语法,可以将构造函数参数变成具有相同名称和值的类属性。这些被称为参数属性,通过在构造函数参数前加上可见性修饰符 public 、 private 、 protected 或 readonly 来创建。由此产生的字段会得到这些修饰符。

    class Params { 
    	constructor(public readonly x: number, protected y: number, private z: number) 
    	{
    		// No body necessary 
    	} 
    }
    const a = new Params(1, 2, 3); 
    // (property) Params.x: number 
    console.log(a.x); 
    

7.类表达式

const someClass = class<Type> {   //这是一个类表达式,与类声明非常相似,类表达式不需要名字,我们可以通过它们最终绑定的任何标识符来引用它们
    content: Type; constructor(value: Type) { 
        this.content = value; 
    } 
};
// const m: someClass 
const m = new someClass("Hello, world");

8.抽象类和成员

abstract class Base { 
    abstract getName(): string; 
    printName() { 
        console.log("Hello, " + this.getName()); 
    } 
}

const b = new Base(); //错误

class Derived extends Base { 
    getName() { return "world"; } 
}

const d = new Derived(); 
d.printName();

function greet(ctor: new() => Base) { 
    const instance = new ctor(); 
    instance.printName(); 
}

greet(Derived); 
greet(Base); //错误

9.类之间的关系

  • TypeScript中的类在结构上与其他类型相同,是可以比较的。
  • 即使没有明确的继承,类之间的子类型关系也是存在的。

你可能感兴趣的:(Typescript,typescript,javascript,前端)