关于typescript:重温Typescript

4次阅读

共计 19739 个字符,预计需要花费 50 分钟才能阅读完成。

对 TS 有些陌生了,想着来总结一波,可作为日后工作中的疾速参考手册。

TypeScript 具备类型零碎,且是 JavaScript 的超集。

它能够编译成一般的 JavaScript 代码。TypeScript 反对任意浏览器,任意环境,任意零碎并且是开源的。

TS 是 JS 的超集,我的了解是:JS + 类型零碎 = TS,对于相熟 JS 的同学来说,TS 的学习老本很低!

我的项目引入 TS 益处也很显著:

  • 让程序更加强壮,更易保护。
  • 在编译器中的类型提醒让一些低级谬误在编译阶段就被发现。
  • 防止一些有效的沟通。

👂🏻据说你很会 Ts? 来,让我看看👁👁!!

初学者倡议移步【TS 中文网】进行学习

若行文有误,欢送指出👏🏻👏🏻👏🏻

一些筹备工作

这些工作更多的是为了测试 ts 个性。
当然这不是必须的,也能够到 Typescript 练习场 去练习和测试。

装置 typescriptts-node。前者是装置一个 ts 环境,将编译 ts 文件为 js, 不便查看其编译状况,后者反对终端运行 ts 文件。

npm install -g  typescript # 全局装置 ts

tsc -v # 查看装置的版本
# Version 5.0.3

npm install -g ts-node # 全局装置 ts-node

ts-node -v
# v10.9.1

新建一个名 summary 的目录,进入目录新建 helloTS.ts 文件。

mkdir summary
cd summary
touch helloTs.ts

下面步骤进行顺利的话,此时能够初始化目录、编译文件、运行文件了。

helloTs.ts 里写下一点想法

const hello: string = '你好,ts, 我顶你个肺啊,哈哈哈'

终端

tsc --init # 初始化,同目录下会生成 tsconfig.json 文件

tsc helloTs.ts # 编译,首次运行会在同目录下生成 helloTs.js 文件,这外面就是编译的后果

ts-node helloTs.ts # 执行 helloTs.ts 文件,有点像 node xxx.js

tsconfig.json 文件定义着 会对哪些文件进行编译,如何编译(细则,束缚),见最初。

根底类型

变量类型申明 是 冒号 + 类型 对一个变量进行类型申明的(废话。

js 的八大类型

let str: string = "ethan";
let num: number = 24;
let bool: boolean = false;
let und: undefined = undefined;
let nul: null = null;
let obj: object = {x: 1};
let big: bigint = 100n;
let sym: symbol = Symbol("me"); 

null && undefined

这两个值略微非凡些,null,undefined 是所有类型的子类型, 即能够赋值给任何类型

let obj:object ={};
obj = null;
obj= undefined;

const und: undefined = undefined;
const nul: null = null;
let str: string = nul;
let num: number = und;

如果开启严格模式:在 tsconfig.json 指定了 "strict": true,"strictNullChecks":true,运行会抛错,在编译器里就会有提醒。

let aa: string = nul;// ❌不能将类型“null”调配给类型“string”let b: number = und;// ❌不能将类型“undefined”调配给类型“number”。

空值(void

void 意思就是有效的, 个别作用在函数上,通知他人这个函数没有返回值

function sayHello(): void {console.log("hello ts");
}

须要留神 null 赋值给 void 值在严格模式下会抛错

let c:void = undefined // 编译正确
let d:void = null // 严格模式下: ❌不能将类型“null”调配给类型“void”

never

never类型示意的是那些永不存在的值的类型。例如,never 类型是那些总是会抛出异样或基本就不会有返回值的函数表达式或箭头函数表达式的返回值类型。

// 返回 never 的函数必须存在无奈达到的起点
function error(message: string): never {throw new Error(message);
}

// 推断的返回值类型为 never
function fail() {return error("Something failed");
}

// 返回 never 的函数必须存在无奈达到的起点
function infiniteLoop(): never {while (true) {}}

枚举(enum

应用枚举咱们能够定义一些有名字的数字常量。枚举通过 enum 关键字来定义。

// 不赋值,默认从 0 开始~~
// 赋值后即从该值往后 顺次累加
enum Anmimal {
  Cat,// 0
  Dog = 2,
  Num = Dog * 2, // 4
  Bird // 5,G = '123'.length // 计算成员
}
const cat: Anmimal = Anmimal.Cat;
const dog: Anmimal = Anmimal.Dog;
const num: Anmimal = Anmimal.Num;
const bird: Anmimal = Anmimal.Bird;
console.log(cat, dog, num, bird) // 0 2 4 5

异构枚举(Heterogeneous enums),指枚举值同时呈现字符串和数字,尽量避免。

enum BooleanLikeHeterogeneousEnum {
  No = 0,
  Yes = "YES",
}

常量枚举 (constant enum),应用 const 关键字润饰的 常量枚举 在编译后会被删除

const enum Color {
  RED,
  PINK,
  BLUE,
}

const color: Color[] = [Color.RED, Color.PINK, Color.BLUE];
console.log(color); //[0, 1, 2]

// 编译后
var color = [0 /* Color.RED */, 1 /* Color.PINK */, 2 /* Color.BLUE */];
console.log(color); //[0, 1, 2]

运行时枚举(Enums at runtime)

enum E {X, Y, Z}
function f(obj: { X: number}) {return obj.X;}
// Works, since 'E' has a property named 'X' which is a number.
console.log(f(E)); // 0

编译时枚举(Enums at compile time)

enum LogLevel {ERROR, WARN, INFO, DEBUG,}
type LogLevelStrings = keyof typeof LogLevel; // 'ERROR' | 'WARN' | 'INFO' | 'DEBUG'
function printImportant(key: LogLevelStrings, message: string) {const num = LogLevel[key];
  if (num <= LogLevel.WARN) {console.log("Log level key is:", key); // Log level key is: ERROR
    console.log("Log level value is:", num); // Log level value is: 0
    console.log("Log level message is:", message); // Log level message is:This is a message
  }
}
printImportant("ERROR", "This is a message");

任意值(any

家喻户晓,TS 的 any 大法无所不能,哈哈哈~~~~(玩笑

当在编程阶段给还不分明类型的变量指定一个类型。这些值可能来自于动静的内容,比方来自用户输出或第三方代码库,那就须要应用 any 了。

any 类型可赋值给任何类型,并承受任何类型赋值。

let notSure: any = 4;
notSure = "maybe a string instead";
notSure = false; // 可承受二次类型赋值
let str:string = notSure;// any 可给任意类型赋值

let obj: any = 4;
obj.ifItExists(); // ifItExists 办法兴许在运行时存在
obj.toFixed(); // toFixed 存在 (然而编译时不查看)

let list: any[] = [1, true, "free"];

unknown

unknownany 一样,所有类型都能够调配给 unknown

let value: unknown = 1;
value = "hello"; 
value = false; 

any 不同的是,unknown 的值只能赋值给 unknown any

let unv: unknown = 1;
let anyv: any = unv;
let str: string = unv; // ❌不能将类型“unknown”调配给类型“string”。

对象类型

在 JavaScript 中,咱们分组和传递数据的根本形式是通过对象。在 TypeScript 中,咱们通过对象类型(泛指)来示意这些类型。

这里的对象很形象,记得刚学 java 时,老师说过:“万物皆可为对象~~”。你懂我意思吧😂。

数组

数组两种申明形式

const arr: number[] = [1,2];
const arr1:Array<number> = [1,2]; 

对象类型

这个对象就是实实在在的,具体的,对象分为 objectObject{}

object 仅仅指一个对象,不承受其余根底值的赋值,包含(null, undefined)。

let object: object;
// object = 1; // 报错:不能将类型“number”调配给类型“object”// object = "a"; // 报错:不能将类型“string”调配给类型“object”。object = {}; // 编译正确

Object 代表所有领有 toStringhasOwnProperty 办法的类型,所以所有原始类型、非原始类型都能够赋给 Object(严格模式下 null 和 undefined 不能够)。{}同样, 能够认为是 Object 的简写

let bigObj: Object;
bigObj = 1;
bigObj = "a";

let normalObj: {};
normalObj = 1;
normalObj = "a";

let object: object;
normalObj = {};

{} 和大 Object能够相互代替,用来示意原始类型(null、undefined 除外)和非原始类型;而小 object 则示意非原始类型。object 更像是 Object 的子集。

另外留神,除了 Oject 大小写能够互相赋值,而其余根底类型(number、string、boolean、symbol)大写赋值给小写会抛错,且没有意义。

❌ Don’t ever use the types Number, String, Boolean, Symbol, or Object These types refer to non-primitive boxed objects that are almost never used appropriately in JavaScript code.

—-
https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#general-types

元祖(Tuple

元组类型容许示意一个 已知元素数量和类型 的数组,各元素的类型不用雷同。能够了解为数组的裁减

const tuple: [string, number] = ['哈哈', 1];

type Either2dOr3d = [number, number, number?];// number 可选

// ... 示意 0 或多个 boolean 值 的元素
type StringNumberBooleans = [string, number, ...boolean[]];
type StringBooleansNumber = [string, ...boolean[], number];
type BooleansStringNumber = [...boolean[], string, number];

// 只读元祖
const roTuple: readonly [string, number] = ['哈哈', 1]

扩大类型(Extending Types

对某一个类型 应用 extends 进行扩大

interface BasicAddress {
  name?: string;
  street: string;
  city: string;
  country: string;
  postalCode: string;
}

interface AddressWithUnit extends BasicAddress {unit: string;}

const liveAdress: AddressWithUnit = {
  unit: "ts",
  name: "ethan",
  street: "secret",
  city: "secret",
  country: "china",
  postalCode: "dont know",
}

穿插类型(Intersection Types

穿插类型 就是跟 联结类型 (Union Types,前面提到) 相同,用 & 操作符示意,穿插类型就是两个类型必须存在

interface PersonA{ // interface 为接口
  name: string,
  age: number,
  birth?: Date; // 生日:YYYY-MM-DD
  hobby?: "football" | "tennis"; // 喜好 只能为 "football" 或 "tennis"
}

interface PersonB {
  name: string,
  gender: string
}

type PersonAWithBirthDate = PersonA & {birth: Date};

let person: PersonA & PersonB = { 
    name: "ethan",
    age: 18,
    gender: "男"
};

只读数组(ReadonlyArray Type

此类型的数组不能被扭转

const roArray: ReadonlyArray<string> = ["red", "green", "blue"];
roArray[0] = 'change' // ❌类型“readonly string[]”中的索引签名仅容许读取
roArray.push('grey'); // ❌类型“readonly string[]”上不存在属性“push”。

以上状况 与只读修饰符 的成果一样

const roArray: readonly string[] = ["red", "green", "blue"];

最初须要留神的一点是,与只读属性修饰符不同,可赋值性在惯例数组和只读数组之间不是双向 (不可调配 / 赋值) 的。

let xx: readonly string[] = [];
let yy: string[] = [];
 
xx = yy; // ok
yy = xx; // ❌类型 "readonly string[]" 为 "readonly",不能调配给可变类型 "string[]"。roArray = yy; // 因为 const 定义的。❌类型 "readonly string[]" 为 "readonly",不能调配给可变类型 "string[]"
yy = roArray; // ❌类型 "readonly string[]" 为 "readonly",不能调配给可变类型 "string[]"。

函数

js 的一等公民,应用场景太多了

函数:反对给参数、返回值定义类型;默认参数仍然实用;? 示意 y 是可选参数

function func(x: number, def: boolean = false, y?: number): number {return y ? x + y : x; // 对于可选参数须要做判断}

// 接口定义函数,前面讲
interface Add {(x: number, y: number): number;
}

// 申明一个箭头函数,=> 前面 number 约定的是返回值类型
let Add: (baseValue: number, increment: number) => number =
    function(x: number, y: number): number {return x + y;};

反对残余参数定义类型

function restFunc(...numbers: number[]): number {
  let sum = 0;
  for (let i = 0; i < numbers.length; i++) {sum += numbers[i];
  }
  return sum;
}

函数重载,函数重载真正执行的是同名函数最初定义的函数体,在最初一个函数体定义之前全都属于函数类型定义,不能写具体的函数实现办法,只能定义类型

function add(x: number, y: number): number;
function add(x: string, y: string): string;
function add(x: any, y: any): any {return x + y;}

约定了 add 接管的参数为 一旦有数字或字符串,两个参数必须同为数字或字符串,且返回值也应该为数字或字符串。相当于约定了参数的一致性(入参,出参),当然这里只是演示,并不是说他的作用只有这样,毕竟入参和出参本人定义,但应该是一个强绑定的。

类型推断

对某个变量间接赋值,那么扭转量的类型就会被推断为该值的类型

let number = 1;// 被推断为 number 类型

如果定义的时候没有赋值,不论之后有没有赋值,都会被推断成 any 类型而齐全不被类型查看.

let value;
value = 'ethan';
value = 7;

类型断言

对于某些数据,我比 TS 更加理解它的类型,那么这时候类型断言就实用。

类型断言:手动指定一个值的类型

根本语法

反对两种形式

// 形式 1
let str: any = "to be or not to be";
let strLength: number = (<string>str).length;
// 形式 2
let str1: any = "to be or not to be";
let strLength1: number = (str1 as string).length;

非空断言

对于多类型的数据,某个场景下无奈依据上下文确定其类型,那么非空断言能够反对排除 nullundefined 值,防止应用时不必要的判断。语法是 一个后缀表白符号 !(英文感叹号)

// 场景 1
let value: null | undefined | string;
value!.toString();
value.toString(); // ❌value 可能为“null”或“未定义”。// 场景 2
type funcType = () => number
function myFunc(func: funcType | undefined) {const num1 = func(); //“func”可能为“未定义”const num2 = func!();}

确定赋值断言

某个变量被定义后,未赋值前应用就会报错,这时候能够利用确定赋值断言解决。相当于通知 TS, 我前面会赋值的,你别瞎操心了,哈哈。

let value:number;
// 以下语句未赋值应用就会抛错
console.log(value); // ❌在赋值前应用了变量“value”。// 应用赋值断言
let value!:number;
console.log(value); // undefined

联结类型(Union Types

将二种及以上多种类型用 | 隔开的类型称为联结类型

let union: string | number; // union 反对赋值 string 或 number

type U = string[] | string; // 反对字符串数组或字符串

条件类型(Conditional Types

简略了解就像 js 的 三目运算符. 联合代码很好了解。

SomeType extends OtherType ? TrueType : FalseType;

根本应用

interface Animal {live(): void;
}
interface Dog extends Animal {woof(): void;
}
 
type Example1 = Dog extends Animal ? number : string; // type Example1 = number
 
type Example2 = RegExp extends Animal ? number : string; // type Example2 = string

在某些状况下,能够防止写简单的重载函数。上面是一个重载函数示例

interface IdLabel {id: number;}
interface NameLabel {name: string;}
 
function createLabel(id: number): IdLabel;
function createLabel(name: string): NameLabel;
function createLabel(nameOrId: string | number): IdLabel | NameLabel;
function createLabel(nameOrId: string | number): IdLabel | NameLabel {throw "unimplemented";}

利用条件类型改一下

type nameOrId<T extends number | string> = T extends number
  ? IdLabel
  : NameLabel;
function createLabel<T extends number | number>(idOrName: T): NameOrId {thorw "unimplemented";}

能够看到 nameOrId 做到了 入参类型不同,出参类型主动判断。很显著在只有两种条件的状况下很便捷。

泛型限度

与泛型联合应用时能更好的束缚泛型。

type MessageOf<T> = T extends {message: unknown} ? T["message"] : never;

type Flatten<T> = T extends any[] ? T[number] : T;

调配条件类型

当咱们向条件类型传入联结类型,如果没有应用 [] 会呈现映射联结类型的每个成员类型。

type ToArray<T> = T extends any ? T[] : never;
type ToArray2<T> = [T] extends [any] ? T[] : never;

type StrArrOrNumArr = ToArray<string | number>; // string[] | number[]
type StrArrOrNumArr2 = ToArray2<string | number>; // (string | number)[]

类型别名

类型别名用 type 来给一个类型起个新名字。它只是起了一个新名字,并没有创立新类型。类型别名罕用于联结类型。

type personType = {
   name: string,
   age: number  
}
let Ethan: personType;

type count = number | number[];
function hello(value: count) {}

类型爱护(Type Guards

类型爱护是运行时查看某些值,确保某些值在所要类型的范畴内。(联合代码示例更不便了解

类型爱护有四种形式

1,is(自定义类型爱护)

function getLength(arg: any): pet is string {return arg.length;}

2,typeof, 利用判断去做

function getType(val: any) {if (typeof val === "number") return 'number'
  if (typeof val === "string") return 'string'
  return '啥也不是'
}

3,instanceof, instanceof类型爱护 * 是通过构造函数来细化类型的一种形式

function creatDate(date: Date | string){console.log(date)
  if(date instanceof Date){date.getDate()
  }else {return new Date(date)
  }
}

4,in

type Fish = {swim: () => void };
type Bird = {fly: () => void };

function move(pet: Fish | Bird) {if ("swim" in pet) { // 如果 pet 里存在 swim 办法则为 Fish,就游起来
    return pet.swim();}
  return pet.fly(); // 否则肯定为 Bird,就飞起来}

接口(interface

这,是个大玩意儿。

在 TypeScript 里,接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

接口命名个别首字母大写。

根本用法

// 此接口囊括了接口的大部分个性
interface InterA{
  prop1: string,
  prop2?: number, // 可选参数
  readonly prop3: boolean // 只读参数
  readArr: ReadonlyArray<number> // 只读数组,一经定义不能被批改
  birth?: Date; // 生日(可选):YYYY-MM-DD
  hobby?: "football" | "tennis"; // 喜好(可选),只能为 "football" 或 "tennis"
  // 以下应用来扩大未知字段的,也就是说此接口除了以上字段还有许多不确定的字段。[prop: string | number]: any; // 索引签名,prop 字段必须是 string 类型 or number 类型。}

扩大

接口反对 extends 扩大更多的属性

interface InterA {// props}
// InterB 在 InterA 的根底上扩大了 propb 属性
interface InterB extends InterA {propb: string}

扩大多类型

interface Colorful {color: string;}
interface Circle {radius: number;}

interface ColorfulCircle extends Colorful, Circle {}

const cc: ColorfulCircle = {
  color: "red",
  radius: 42,
};

接口(interface)和类型 (type) 的区别

概念上,interface 是一种具体的类型,而 type 仅是类型的一种别名。

大多数状况下,此两者应用起来差不多。都能够用来形容对象 / 函数的类型,都能够扩大

这里有一些区别

1,语法

// 定义
type MyTYpe = {
  name: string;
  say(): void;}

interface MyInterface {
  name: string;
  say(): void;}

// 扩大
type MyType2 = MyType & {sex:string;}
let person:MyInterface2 = {
  name:'ethan',
  sex:'男',
  say(): void { console.log("hello ts") }
}

interface MyInterface2 extends MyInterface {sex: string;}
let person1:MyInterface2 = {
  name:'ethan',
  sex:'男',
  say(): void { console.log("hello ts") }
}

当然,也反对相互扩大

interface MyInterface3 {sex: string;}

type mixType = MyType & MyInterface3
interface mixInterface extends MyType {sex: string}

下面可能是小不同,上面的不同可能让它两在各自场景能各尽其用:

  • type 能够申明根本数据类型别名 / 联结类型 / 元组等,而 interface 不行
// 根本类型别名
type UserName = string;
type UserName = string | number;
// 联结类型
type Animal = Pig | Dog | Cat;
type List = [string, boolean, number];
  • interface 可能合并申明,而type 不行
interface Person {name: string}
interface Person {age: number}
// 此时 Person 同时具备 name 和 age 属性

泛型(Generics

泛型是指在定义函数、接口或类的时候,不预先指定具体的类型,而在应用的时候再指定类型的一种个性。

假如一个场景:一个函数须要入参出参都是 string。可能会这样写

function func(arg:string):string {return arg}

当初需要改了,除了反对 string,还得反对 number, 你可能会这样写

function func(arg:string | number):string | number {return arg}

某日,需要又至矣,修修改改无穷尽也??吾心有一马奔腾不止。。。
泛型就是解决这样的问题

根本应用

function func<T>(arg:T):T  {return arg;}
func<string | number>('哈哈哈');
func('哈哈哈');// 不指定类型,ts 会主动推断

let myIdentity: <U>(arg: U) => U = identity;

泛型中的 T 就像一个占位符、或者说一个变量,在应用的时候能够把定义的类型 像参数一样传入 ,它能够 一成不变地输入

Array 本身也是个泛型。

interface Array<Type> {
  length: number;
  pop(): Type | undefined;
  push(...items: Type[]): number;
  // ...
}

多个参数

function func<T, U>(arg:[T,U]):[T,U] {return arg;}
func("ethan", 18);// ["ethan", 18]

泛型束缚

即预先指定该泛型满足某些条件

示例 1 根本用法

// 应用 extends 束缚泛型
interface Lengthwise {length: number;}
function getLength1<T extends Lengthwise>(arg:T):T  {console.log(arg.length); 
  return arg;
}

const str = getLength('ethan')
const arr = getLength([1,2,3])
const obj = getLength({length: 1})
const num = getLength1(1) // ❌类型“number”的参数不能赋给类型“Lengthwise”的参数。

示例 2 泛型束缚中应用类型参数

function getProperty<T, K extends keyof T>(obj: T, key: K) {return obj[key];
}
let x = {a: 1, b: 2, c: 3, d: 4};
getProperty(x, "a");
getProperty(x, "m"); // ❌类型“"m"”的参数不能赋给类型“"a" | "b" | "c" | "d"”的参数泛型接口

泛型接口

演示 接口与泛型 如何搭配

interface GenericIdentityFn<T> {(arg: T): T;
}

function identity<T>(arg: T): T {return arg;}

let myIdentity: GenericIdentityFn<number> = identity;

泛型类

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) {return x + y;};

泛型别名

type Cart<T> = {list: T[] } | T[];
let c1: Cart<string> = {list: ["1"] };
let c2: Cart<number> = [1];

泛型工具类型

1,typeof,typeof 的主要用途是在类型上下文中获取变量或者属性的类型

let p1 = {name: "ethan", age: 18, isAdult: true};
type People = typeof p1; // 返回的类型个别用 type 承接取个别名
function getName(p: People): string {return p.name;}
getName(p1);

// typeof p1 编译后
type People = {
  name: string,
  age: number,
  isAdult: boolean,
};

如果 p1 为嵌套对象也实用。

2, keyof,此操作符是获取某种类型的所有键,返回的是联结类型。

interface Person {
  name: string;
  age: number;
}

type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join" 
type K3 = keyof {[x: string]: Person };  // string | number

// 也反对根底类型
let K1: keyof boolean; // let K1: "valueOf"
let K2: keyof number; // let K2: "toString" | "toFixed" | "toExponential" | ...
let K3: keyof symbol; // let K1: "valueOf"

3,in,用以遍历枚举类型

type Keys = "a" | "b" | "c"

type Obj =  {[p in Keys]: any
} // -> {a: any, b: any, c: any}

4, infer,在条件类型语句中,申明类型变量以应用它。

type ReturnType<T> = T extends (...args: any[]
) => infer R ? R : any; // R 承载了返回值的类型

5,extends,为泛型减少束缚, 参考之前泛型。

interface Lengthwise {length: number;}

function loggingIdentity<T extends Lengthwise>(arg: T): T {console.log(arg.length);
  return arg;
}

6,[Type][key],索引拜访操作符

interface Person {
  name: string;
  age: number;
}

type Name = Person["name"]; // string

实用程序类型(Utility Types

1,Required,将类型的属性变成必选

Required<Type>

interface Person {
    name?: string,
    age?: number
}
const person: Required<Person> = {
    name: "ethan",
    age: 18
}

2,Partial,与 Required 相同,将所有属性转换为可选属性

Partial<Type>

interface Person {
    name: string,
    age: number
}
type User = Partial<Person>
const shuge: User = {// 输出 Person 的某些属性
  name:'ethan'
}

3,Extract,相当于取交加(参 1∩参 2)

Extract<Type, Union>

type T01 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
type T11 = Extract<string | number | (() => void), Function>; // () =>void

4,Exclude,相当于取差集(参 1 – 参 1∩参 2)

Exclude<UnionType, ExcludedMembers>

type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number

5,Readonly,将数组或对象的所有属性值转换为只读。

Readonly<Type>

interface Todo {title: string;}
const todo: Readonly<Todo> = {title: "Delete inactive users",}
todo.title = "Hello"; // ❌无奈为“title”赋值,因为它是只读属性。

6,Record, 将参 2 的类型一一赋给参 1 的 key

Record<Keys, Type>,等同于

type newType = {[key keyof keys]: Type
}
interface CatInfo {
  age: number;
  breed: string;
}
type CatName = "miffy" | "boris" | "mordred";
const cats: Record<CatName, CatInfo> = {miffy: { age: 10, breed: "Persian"},
  boris: {age: 5, breed: "Maine Coon"},
  mordred: {age: 16, breed: "British Shorthair"},
};

7,Pick,从某些类型外面挑一些属性进去, 不便复用

Pick<Type, Keys>

type Person = {
  name: string;
  age:number;
  gender:string
}

type P1 = Pick<Person, "name" | "age">; // {name: string; age: number;}

8,Omit,与 Pick 相同,从某些类型外面剔除一些属性进去, 不便复用

Omit<Type, Keys>

interface Person {
  name: string,
  age: number,
  gender: string
}
type P1 = Omit<Person, "age" | "gender"> // {name: string}

9,ReturnType,获取函数返回值类型

ReturnType<Type>

type T0 = ReturnType<() => string>; // string

function f1(): { a: number; b: string};
type T1 = ReturnType<typeof f1>; // {a: number; b: string}

type T2 = ReturnType<<T extends U, U extends number[]>() => T>;// number[]

10,NonNullable, 去除 null 和 undefined 类型

Parameters<Type>

type P1 = NonNullable<string | number | undefined>; // string | number
type P2 = NonNullable<string[] | null | undefined>; // string[]

11,Paramenters, 获取函数类型的参数类型,后果是一个元祖(Tuple

Parameters<Type>

type T0 = Parameters<() => string>; // []

function f1(arg: { a: number; b: string}): void;
type T1 = Parameters<typeof f1>; // [arg: { a: number; b: string}]

12, InstanceType, 获取 构造函数的实例类型

InstanceType<Type>

class C {
  x = 0;
  y = 0;
}
type T0 = InstanceType<typeof C>; // C
type T1 = InstanceType<any>; // any

13, Awaited, 比拟新,获取异步函数中的 await 或 Promises 上的.then()办法 返回的类型.

Awaited<Type> | Released: 4.5

type A = Awaited<Promise<string>>; // string

type B = Awaited<Promise<Promise<number>>>; // number

type C = Awaited<boolean | Promise<number>>; // number | boolean

我的了解是为了更好的解决 async 函数内 await 的后果类型。

async function foo<T>(x: T) {
    const y: Awaited<T> = await x;
    // const y: Awaited<T>
    // const y: T before TS4.5
}

参考
What is the Awaited Type in TypeScript – Stack Overflow

https://www.typescriptlang.org/play

14,大小写转换

Uppercase<StringType> | 全大写

Lowercase<StringType> | 全小写

Capitalize<StringType> | 首字母写

Uncapitalize<StringType> | 非首字母大写

很好了解,为了更加准确的规定字符串类型的值,值也是一种类型嘛。

type Greeting = "Hello, world";
type ShoutyGreeting = Uppercase<Greeting>;
// type ShoutyGreeting = "HELLO, WORLD"
const helloStr: ShoutyGreeting = "HELLO, WORLD";
const helloStr: ShoutyGreeting = "hello";// ❌不能将类型“"hello"”调配给类型“"HELLO, WORLD"”。type ASCIICacheKey<Str extends string> = `ID-${Uppercase<Str>}`;
type MainID = ASCIICacheKey<"my_app">;
// type MainID = "ID-MY_APP"

type Greeting = "Hello, world";
type QuietGreeting = Lowercase<Greeting>;
// type QuietGreeting = "hello, world"

type LowercaseGreeting = "hello, world";
type Greeting = Capitalize<LowercaseGreeting>;
// type Greeting = "Hello, world"

type UppercaseGreeting = "HELLO WORLD";
type UncomfortableGreeting = Uncapitalize<UppercaseGreeting>;
// type UncomfortableGreeting = "hELLO WORLD"

还有更多的实用工具类,不一一例举了,可见 官网

配置文件(tsconfig.json

次要字段

  • extends – 继承配置,例 “extends”: “./configs/base”,
  • files – 设置要编译的文件的名称;
  • include – 设置须要进行编译的文件,反对门路模式匹配;
  • exclude – 设置无需进行编译的文件,反对门路模式匹配;即便 include 蕴含了,但不能排除 files 的文件
  • compilerOptions – 设置与编译流程相干的选项,初始化的时候只会有此字段。

compilerOptions

{
  "compilerOptions": {

    /* 根本选项 */
    "target": "es5",           // 指定 ECMAScript 指标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
    "module": "commonjs",      // 指定应用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
    "lib": [],                 // 指定要蕴含在编译中的库文件
    "allowJs": true,           // 容许编译 javascript 文件
    "checkJs": true,           // 报告 javascript 文件中的谬误
    "jsx": "preserve",         // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
    "declaration": true,       // 生成相应的 '.d.ts' 文件
    "sourceMap": true,         // 生成相应的 '.map' 文件
    "outFile": "./",           // 将输入文件合并为一个文件
    "outDir": "./",            // 指定输入目录
    "rootDir": "./",           // 用来管制输入目录构造 --outDir.
    "removeComments": true,    // 删除编译后的所有的正文
    "noEmit": true,            // 不生成输入文件
    "importHelpers": true,     // 从 tslib 导入辅助工具函数
    "isolatedModules": true,   // 将每个文件做为独自的模块(与 'ts.transpileModule' 相似).

    /* 严格的类型查看选项 */
    "strict": true,            // 启用所有严格类型查看选项
    "noImplicitAny": true,     // 在表达式和申明上有隐含的 any 类型时报错
    "strictNullChecks": true,  // 启用严格的 null 查看
    "noImplicitThis": true,    // 当 this 表达式值为 any 类型的时候,生成一个谬误
    "alwaysStrict": true,      // 以严格模式查看每个模块,并在每个文件里退出 'use strict'

    /* 额定的查看 */
    "noUnusedLocals": true,                // 有未应用的变量时,抛出谬误
    "noUnusedParameters": true,            // 有未应用的参数时,抛出谬误
    "noImplicitReturns": true,             // 并不是所有函数里的代码都有返回值时,抛出谬误
    "noFallthroughCasesInSwitch": true,    // 报告 switch 语句的 fallthrough 谬误。(即,不容许 switch 的 case 语句贯通)/* 模块解析选项 */
    "moduleResolution": "node",            // 抉择模块解析策略:'node' (Node.js) or 'classic' (TypeScript pre-1.6)
    "baseUrl": "./",                       // 用于解析非绝对模块名称的基目录
    "paths": {},                           // 模块名到基于 baseUrl 的门路映射的列表
    "rootDirs": [],                        // 根文件夹列表,其组合内容示意我的项目运行时的构造内容
    "typeRoots": [],                       // 蕴含类型申明的文件列表
    "types": [],                           // 须要蕴含的类型申明文件名列表
    "allowSyntheticDefaultImports": true,  // 容许从没有设置默认导出的模块中默认导入。/* Source Map Options */
    "sourceRoot": "./",          // 指定调试器应该找到 TypeScript 文件而不是源文件的地位
    "mapRoot": "./",             // 指定调试器应该找到映射文件而不是生成文件的地位
    "inlineSourceMap": true,     // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
    "inlineSources": true,       // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性

    /* 其余选项 */
    "experimentalDecorators": true,        // 启用装璜器
    "emitDecoratorMetadata": true          // 为装璜器提供元数据的反对
  }
}

结尾

github 下面有个 类型体操 的我的项目,适宜练习,进阶。

参考了一些网上的博客,甚至有些例子是间接 copy, 非常感谢👏👏👏。

5 分钟理解 TypeScript – TypeScript 中文手册

https://www.typescriptlang.org/

2022 年了,我才开始学 typescript,晚吗?(7.5k 字总结)– 掘金

2022 typescript 史上最强学习入门文章(2w 字) – 掘金

「1.9W 字总结」一份通俗易懂的 TS 教程,入门 + 实战!– 掘金

TypeScript 类型操作 – 掘金

正文完
 0