TypeScript简单使用及进阶教程

数据类型

基础数据类型

js 基础数据类型

// 不标注类型, 也会自动去做类型推断 

let str: string = "jimmy";

let num: number = 24;

let bool: boolean = false;

let u: undefined = undefined;

let n: null = null;

let obj: object = {x: 1};

let big: bigint = 100n;

let sym: symbol = Symbol("me");

ts 新增类型

/**
 * 任何类型的值可以赋值给any,同时any类型的值也可以赋值给任何类型
 * 任何类型的值都可以赋值给unknown,但 unknown只能赋值给unknown 和 any
 */

// unknown --- 所有类型都可以赋值给 unknown
let un: unknown;

// any  --- 所有类型都可以赋值给 any
let val: any;
/**
 * never 表示的是那些永不存在的值的类型
 * never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型
 */

// 异常
function error(msg: string): never { // 编译正确
  throw new Error(msg); 
}

// 死循环
function loopForever(): never { // 编译正确
  while (true) {};
}

// key不存在
type A<V, T> = {
  [K in keyof V as V[K] extends T ? K : never]: V[K];
};

数组

let arr1: number[] = [1, 2, 3, 4]

let arr2: Array<string> = ['1', '2', '3', '4']


interface ArrayNumber {
    [index:number]: string
}
let arr3: ArrayNumber = ['a', 'b']

let arr4: (number | string)[] = ['1', 2]

let arr5: Array<number | string> = [1, '2']


/**
 * 元组 tuple --- 特殊数组
 * 
 * 元组类型只能表示一个已知元素数量和类型的数组,长度已指定,越界访问会提示错误
 * 
 * 可以解构赋值
 * 
 * 可以定义可选
 * 
 * 定义剩余元素
 * 
 * readonly控制只读
 * */ 
let tuple: readonly [string, number, number?, ...string[]]; 

tuple = ['hello', 10];
tuple = ['hello', 10, 10];
tuple = ['hello', 10, 10, '10'];

// 解构
let [name, age] = tuple;

// 设置了 readonly 修改值 --- error
/* tuple[0] = '2'
tuple.push('3'); */

对象

{} Object 用来表示 原始类型(null、undefined 除外)和 非原始类型

object 则表示 非原始类型

object

object 类型用于表示所有的 非原始类型 ,即我们不能把 number、string、boolean、symbol等 原始类型赋值给 object。在严格模式下,null 和 undefined 类型也不能赋给 object

let object: object;
object = {}; // ok

object = 1; // 报错
object = "a"; // 报错
object = true; // 报错
object = null; // 报错
object = undefined; // 报错

Object / {}

Object 或者 {} 代表所有拥有 toStringhasOwnProperty方法的类型。

所有原始类型、非原始类型 都可以 赋给 Object (严格模式下 null 和 undefined 不可以)

let bigObject: Object;
object = 1; // ok
object = "a"; // ok
object = true; // ok
ObjectCase = {}; // ok

object = null; // 报错
ObjectCase = undefined; // 报错

函数

// 函数声明
function sum(x:number, y:number):number {
    return x + y
}

let sum1 = (x:number, y:number): number => {
    return x + y
}

// 函数表达式
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};

// 接口定义函数
interface SearchFunc {
    (source: string, subString: string): boolean;
}

const SearchFuncFn: SearchFunc = (source: string, subString: string): boolean => {
    return false
}


/**
 * 可选参数
 * 参数默认值
 * 剩余参数
 */

const handleFn = ( a: string, b?: string ) => {
    return a + b 
}

const handleFn2 = ( a: string, b: string = '4' ) => {
    return a + b 
}

const handleFn3 = ( a: string, ...str: string[] ) => {
    return a + str.join(',')
}


// 定义类型
interface SetPointIfc {
    (x: number, y: number): void;
}

type SetPointType = (x: number, y: number) => void;

let fn1: SetPointIfc = (x, y) => {}
let fn2: SetPointType = (x) => {}

// error
// let fn3: SetPointType = (x: string) => {}

类型断言

// 尖括号 语法
let str1: any = "this is a string";
let strLength: number = (<string>str1).length;

// as 语法
let str2: any = '123456';
let numberLength: number = (str2 as string).length;

const mtCanvas = document.getElementById('canvas') as HTMLCanvasElement
const mtCanvas2 = <HTMLCanvasElement>document.getElementById('canvas')
// unknown 不知道(不确定)为什么类型
const x = (123 as unknown) as string


/**
 * 非空断言
 * 
 * ! 仅用于断言 它不是 null 和 undefined
 */
function myFunc(str?: string | null) {
  // const num1 = str.length; // error - 'str' is possibly 'null' or 'undefined'
  const num2 = str!.length;
  const num3 = str?.length;

  if (str) {
    const num4 = str.length;
  }
}


let num1: number
// error - Variable 'num1' is used before being assigned
// let sum1 = num1 * 2 // 还没赋值前使用了

let num2!: number
let sum2 = num2 * 2


let userInput: unknown;
let userName: string;
userInput = 5;
userInput = "Hello";

// 需要进行类型检查或类型断言
if (typeof userInput === "string") {
    userName = userInput; // 这里可以赋值,因为已经进行了类型检查
}

联合 / 交叉 - 类型

联合类型

  • 联合类型表示取值可以为多种类型中的一种,使用 | 分隔每个类型
  • 通常与 nullundefined 一起使用
let variable: string | number;
variable = 'seven';
variable = 7;

// 参数联合类型
const fn = (str: string | null | undefined) => {

}


// 值就是类型
let num: 1 | 2 = 1;

type EventNames = 'click' | 'scroll' | 'mousemove' | '';

交叉类型

  • 交叉类型是 将多个类型合并为一个类型,使用 & 定义交叉类型
  • 交叉类型真正的用武之地就是将多个接口类型合并成一个类型,从而实现等同接口继承的效果,也就是所谓的合并接口类型
  • 同名属性的类型不兼容,会变成 never
// never - 不可能满足
type Useless = string & number;


type PeopleType = { id: number; name: string; } & { age: number };
const mixed: PeopleType = {
  id: 1,
  name: 'name',
  age: 18
}


/* 
  如果同名属性的类型兼容,比如一个是 number,另一个是 number 的子类型、数字字面量类型,
  合并后 name 属性的类型就是两者中的子类型。
*/
type IntersectionTypeConfict = { id: number; name: 2; } 
& { age: number; name: number; };

let mixedConflict: IntersectionTypeConfict = {
  id: 1,
  name: 2,
  age: 2
};
mixedConflict = {
  id: 1,
  // name: 22, // '22' 类型不能赋给 '2' 类型
  name: 2,
  age: 2
};


// 同名属性是非基本数据类型的
interface A {
  x:{d:true},
}
interface B {
  x:{e:string},
}
interface C {
  x:{f:number},
}
type ABC = A & B & C
let abc:ABC = {
  x:{
    d:true,
    e:'',
    f:666
  }
}

类型别名

类型别名, 仅仅是给类型取了一个新的名字, 并不是创建了一个新的类型**

type Point = {
    x: string,
    y: number
}

function fnc1(params: Point) {
    
}

fnc1({
    x: '1',
    y: 1
})

type ID = string | number

let card:ID
card = '123'
card = 123

type Event = 'click' | 'scroll' | 'mousemove'
let handleEvent: Event = 'click'

/**
 * Type 不能同名
 * 使用 & 扩展继承合并
 */
type C = {
    name: string
}

type D = C & {
    sex: boolean
}

let user1: D = {
    name: '张三',
    sex: true
}

接口

可用于[ 对类的一部分行为进行抽象 ]以外,也常用于对「 对象的形状(Shape)」进行描述

定义

/* 定义的必须属性,不允许多,也不允许少 */
interface Person {
    readonly id: string; // 只读
    name: string;
}
let zs: Person = {
    id: '1',
    name: '张三'
}
// 修改只读属性 - error
// zs.id = '2' // Cannot assign to 'id' because it is a read-only property

可选属性

/* 可选属性 */
interface Person2 {
    id: string;
    name?: string; // '?' 表示非必须
}
let zs2: Person2 = {
    id: '1'
}

任意属性

/* 可添加任意属性 */
interface Person3 {
    name: string;
    // 一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
    // [propName: string]: string; 

    age?: number; // 他需要是 任意属性的 子类型 --- 允许是 string

    // 一个接口中只能定义一个任意属性。如果接口中有多个类型的属性,则可以在任意属性中使用联合类型
    [propName: string]: string | number | undefined | null
}

let zs3: Person3 = {
    name: 'Tom',
    age: 25,
    gender: 'male'
};

合并 / 扩展

/**
 * 合并: interface重名合并
 * 扩展: 通过extends继承
 */
interface E {
    name: string
}

interface E {
    sex: boolean
}

let user: E = {
    name: '张三',
    sex: false
}

// 继承一个接口直接 extends
interface F extends E {
    age: number
}

// 继承多个接口用 (逗号 ,) 隔开
// 同样的, 他可以被反复 extends
interface G extends E, F { }

let user2: F = {
    name: '张三',
    sex: false,
    age: 18
}

类型辨定

/** 
 * 类型辨定 --- 鸭式辨型法 
 * */ 

interface LabeledValue {
    label: string;
}
function printLabel(labeledObj: LabeledValue) {
    console.log(labeledObj.label);
}

/* 在参数里写对象就相当于是直接给labeledObj赋值,这个对象有严格的类型定义,所以不能多参或少参。 */
// error
// printLabel({ size: 10, label: "Size 10 Object" });


/* 
    当在外面将该对象用另一个变量myObj接收,myObj不会经过额外属性检查,
    但会根据类型推论为let myObj: { size: number; label: string } = { size: 10, label: "Size 10 Object" };
    然后将这个myObj再赋值给labeledObj,
    此时根据类型的兼容性,两种类型对象,参照鸭式辨型法,
    因为都具有label属性,所以被认定为两个相同,故而可以用此法来绕开多余的类型检查。
*/
// OK
let myObj = { size: 10, label: "Size 10 Object" };
printLabel(myObj);

枚举

  • 只读,不能修改。 — 可以当作一个对象当参数传递使用
  • 无默认值, 从0开始依次递增
  • 指定初始值,依次递增
  • 只要枚举有一个是字符串, 所有成员全部都需要 赋值
enum arr {
    one,
    two,
    three = 5,
    four
}
console.log(arr.one); // 0
console.log(arr.two); // 1
console.log(arr.three); // 5
console.log(arr.four); // 6


// 只要枚举有一个是字符串, 所有成员全部都需要 赋值
enum userStoreEnum {
    TOKEN = 'token',
    USER_INFO = 'userInfo',
    ROLE = 'roles',
}
interface UserInfoType {
    readonly id: string
    name: string
}

// 通过 userStoreEnum[key]获取枚举值
interface UserStoreType {
    [userStoreEnum.TOKEN]: string
    [userStoreEnum.USER_INFO]: UserInfoType | null
    [userStoreEnum.ROLE]: string[]
}

泛型

使用前不确定类型, 使用时才确定类型, 将 使用的类型传入**

常用字母 — 可以用任何有效名称代替

  • TType,在定义泛型时通常用作第一个类型变量名称
  • KKey,对象key类型
  • VValue,对象value值类型
  • EElement,表示元素类型
  • PP in K
  • U: Type<T, U>
// T 是一个抽象类型,只有在调用的时候才确定它的值
function identity<T>(arg: T): T {
  return arg;
}
identity(1) // T的类型是 1
identity<number>(1) // T的类型是 number


// 可以使用任意个数的泛型
function fn<T, U>(value: T, message: U) : {value: T, message: U} {
  return {value, message}
}
// 会自动进行类型推断
fn(68, "Semlinker")

fn<Number, string>(68, "Semlinker")



// 泛型约束 --- 通过 extends 指定这个泛型必须要拥有哪些属性
interface Sizeable {
  size: number;
}
function trace<T extends Sizeable>(arg: T): T {
  console.log(arg.size);
  return arg;
}

工具

typeof

获取变量, 属性, 函数的类型

interface Person {
  name: string;
  age: number;
}
const sem: Person = { name: "semlinker", age: 30 };

type Sem = typeof sem; // type Sem = Person
type NameType = typeof sem.name


const message = {
  name: "jimmy",
  age: 18,
  address: {
    province: '四川',
    city: '成都'   
  }
}
type Message = typeof message;

function toArray(x: number): Array<number> {
  return [x];
}
type Func = typeof toArray; // -> (x: number) => number[]

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"


// 使用情况
type Todo = {
  id: number;
  text: string;
  done: boolean;
}

const todo: Todo = {
  id: 1,
  text: "Learn TypeScript keyof",
  done: false
}

function prop<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

const id = prop(todo, "id");
const text = prop(todo, "text");
const done = prop(todo, "done");
// error
// const other = prop(todo, "other");

in

用来遍历枚举类型。

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

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

infer

在条件类型语句中,可以用 infer 声明一个类型变量并且对它进行使用。

用infer声明一个变量名,占用原有位置的泛型类型

type ReturnType<T> = T extends (
  ...args: any[]
) => infer R ? R : any;

例:

// 获取数组第一个类型
type Head <T extends any[]> = T['length'] extends 0 ? never : T[0];

type Head <T extends any[]> = T extends [head : infer H, ...rest : any[]] ? H : never;

type H0 = Head<[]> // never
type H1 = Head<[1]> // 1
type H2 = Head<[3, 2]> // 3
type H3 = Head<["a", "b", "c"]> // "a"
type H4 = Head<[undefined, "b", "c"]> // undefined
type H5 = Head<[null, "b", "c"]> // null

// 用于获取数组类型除了第一个类型外,剩余的类型。
type Tail<T extends Array<any>> = T extends [infer A, ...infer B] ? B : [];

// 用于把指定类型 E 作为第一个元素添加到 T 数组类型中
type Unshift<T extends any[], E> = T extends [...args: infer U] ? [E, ...U] : never;

// 移除第一个类型
type Shift<T extends any[]> = T extends [infer F, ...infer Rest] ? Rest : T

// 用于把指定类型 E 作为第最后一个元素添加到 T 数组类型中
type Push<T extends any[], V> = T extends [...infer U] ? [...U, V] : never;

extends

泛型约束,可以做三目运算符做判断是否满足约束

type A<V, T> = {
  [K in keyof V as V[K] extends T ? K : never]: V[K];
};

type B<T, K extends string> = {
  [P in keyof T as T[P] extends K ? K : never]: T[P];
}

内置泛型类

以下内容都 只能处理 第一层属性, 不能深度影响

// 测试接口
interface UserInfo {
  readonly id: string;
  name: string;
  age?: number
  hobby?: {
    football: boolean
    basketball: boolean
  }
}

Partial

Partial<T>: 将类型的属性变成可选

type A = Partial<UserInfo>


// 深度可选
type DeepPartial<T> = {
  [U in keyof T]?: 
    T[U] extends object ? // 判断是不是对象
      DeepPartial<T[U]> : // 是则继续递归
      T[U]
}

type B = DeepPartial<UserInfo>

Required

Required<T>: 将类型的属性变成必选

type C = Required<UserInfo>

type DeepRequired<T> = {
  // [U in keyof T]: T[U] extends object ? DeepRequired<T[U]> : T[U]

  // -? 代表 移除 ? 变为必填 ---- 跟不写没什么区别
  [U in keyof T]-?: T[U] extends object ? DeepRequired<T[U]> : T[U]
}

Readonly

Required<T>: 将属性变为只读

type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

type D = Readonly<UserInfo>

Pick

Pick<T>: 从类型中 拿出 想要的属性,获得一个元组类型

type Pick<T, K extends keyof T> = {
  [P in K]: T[P]
}

type PickAttr = Pick<UserInfo, 'id' | 'name'>;
const user1: PickAttr = {
  id: "id",
  name: 'name',
};

Record

Record<K extends keyof any, T>: 将 K 中所有的属性的值转化为 T 类型。

type KeyType = number | string | symbol
type Record<K extends KeyType, T> = {
  [P in K]: T
}

interface PageInfo {
  title: string;
}
type Page = "home" | "about" | "contact";
const x: Record<Page, PageInfo> = {
  about: { title: "about" },
  contact: { title: "contact" },
  home: { title: "home" },
};

ReturnType

ReturnType<T>: 用来得到一个函数的返回值类型

const fnc = () => {
  return 1
}

type MyFnc = typeof fnc

type Fnc = ReturnType<MyFnc>

const Pro = () => {
  return Promise.resolve({
      a: 1,
      b: '2'
  })
}
type c = Awaited<ReturnType<typeof Pro>>

Exclude

Exclude<T, U>: 将某个类型中属于另一个的类型移除掉。

类似 Omit, 但是只能对 联合类型 使用, 对象使用拿到的是 never

type Exclude<T, U> = T extends U ? never : T;

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

Extract

Extract<T, U>: 从 T 中提取出 U。

类似 Pick, 用法 同 Exclude

type Extract<T, U> = T extends U ? T : never;

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



type MyEvent = {
  type: 'click',
  event: MouseEvent
} | {
  type: 'foucs',
  event: FocusEvent
} | {
  type: 'keydown',
  event: KeyboardEvent
}

type REvent = Extract<MyEvent, { type: 'click' }>
type RcEvent = Exclude<MyEvent, { type: 'click' }>

Omit

Omit<T, K extends keyof any> : 使用 T 类型中除了 K 类型的所有属性,来构造一个新的类型。

简单讲就是:剔除掉不要的类型

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type OmitUser = Omit<UserInfo, 'age' | 'hobby'>


/**
 * NonNullable<T> 的作用是用来过滤类型中的 null 及 undefined 类型
 * 
 */
type NonNullable<T> = T extends null | undefined ? never : T;

type T5 = NonNullable<string | number | undefined>; // string | number
type T6 = NonNullable<string[] | null | undefined>; // string[]

Parameters

Parameters<T>: 用于获得函数的参数类型组成的元组类型。

type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;


type T7 = Parameters<(str: string, count: number) => void>; // [str: string, count: number]

type T8 = Parameters<typeof Array.isArray>; // [any]

type T9 = Parameters<typeof parseInt>; // [string, (number | undefined)?]

type T10 = Parameters<typeof Math.max>; // number[]


const obj = (c: {
  a: {
      b: 1
  },
  c: {}
}, d: 2) => {}

type Obj = Parameters<typeof obj>
type CA = Obj[1]

进阶

class Person {
  public readonly id: string // 实例可用 - 只读
  public name: string // 实例可用
  private age?: number // 类本身使用, 对应一些get set方法给外部用
  protected sex?: 'boy' | 'girl' // 类本身和子类使用
  static hobby: Array<string> // 类使用
  constructor(id: string, name: string) {
    this.id = id
    this.name = name
  }
}

class ZhangSan extends Person {
  constructor(id: string, name: string) {
    super(id, name)
    this.sex
  }
}

const zs = new Person('1', '张三')
Person.hobby

抽象类

  • 抽象类描述的是一种抽象的概念,无法被实例化,只能被继承。
  • 抽象类继承在抽象类中,可以不实现,表示属于该抽象类的一个方法,如果继承的非抽象类中,那么抽象类的方法必须需要实现

抽象类对事物进行抽象,更多的是为了继承,为了扩展,为了实现代码的重用

abstract class Animal {
    // 抽象方法,没有具体实现
    abstract makeSound(): void;
 
    // 具体方法
    move(): void {
        console.log('Animals can move.');
    }
}
 
// 实现抽象类
class Dog extends Animal {
    makeSound(): void {
        console.log('Woof! Woof!');
    }
}
 
// 实现抽象类
class Cat extends Animal {
    makeSound(): void {
        console.log('Meow!');
    }
}
 
// 使用
const dog = new Dog();
dog.makeSound(); // 输出: Woof! Woof!
dog.move();      // 输出: Animals can move.
 
const cat = new Cat();
cat.makeSound(); // 输出: Meow!
cat.move();      // 输出: Animals can move.

abstract class Person {
    name: string;
    abstract speak(): void;
}

class Man extends Person {
    // 如果不实现此方法,那么会报错:非抽象类“Man”不会实现继承自“Person”类的抽象成员“speak”
    speak(): void {
        console.log('说话');
    }
}

abstract class Woman extends Person {
    // 抽象类继承抽象类,可以不用具体实现,表述属于抽象类的一个方法
    abstract speak(): void;
}

class Girl extends Woman {
    // 如果不实现此方法,那么会报错:非抽象类“Man”不会实现继承自“Person”类的抽象成员“speak”
    speak(): void {
        console.log('唱歌');
    }
}

// let p = new Person() // 报错:无法创建抽象类的实例
let m = new Man();
m.speak(); // 说话
let g = new Girl();
g.speak(); // 唱歌

接口实现类

使用 implementsclass 类做约束 ,类型使用 , 隔开。

interface AnimalIfc {
  id: string
  name: string
  getName(): string
}
interface Age {
  age: number
}

class Dog implements AnimalIfc, Age {
  id: string
  name: string
  age: number

  constructor(id: string, name: string, age: number) {
    this.id = id
    this.age = age
    this.name = name
  }

  getName(): string {
    return this.id
  }
}

// 获取实例的类型
type ABC = InstanceType<typeof Dog>

泛型类

class BaseDefault<T> {
  public val: T
  constructor(val: T) {
    this.val = val
  }

  getVal(): T {
    return this.val
  }
}

const type = new BaseDefault('2')

常用类型

指定属性可选

interface Article {
  id: string
  name: string
  date: Date
  desc?: string
}

// 基于 Article 的新类型,内容一致,必填属性不一样,重复书写会显得 冗余
/* interface CreateArticle<T> {
  id?: T
  name: string
  date: Date
  desc?: T
} */

// 将 指定 属性变为可选 - 1
type SetPartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>

// 指定可选 - 2
type Simplify<T> = {
  [P in keyof T]: T[P]
}
type SetOptional<T, K extends keyof T> = 
  Simplify<Partial<Pick<T, K>> & Pick<T, Exclude<keyof T, K>>>

// 使用
type CreateArticle = SetPartial<Article, 'id' | 'desc'>
const article: createArticle  = {
  name: '1',
  date: new Date()
}

// 指定必填
type SetRequired<T, K extends keyof T> = Simplify<Pick<T, Exclude<keyof T, K>> & Required<Pick<T, K>>>

其余写法

type SetOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
type SetOptionalOmit<T, K extends keyof T> = Pick<T, K> & Partial<Omit<T, K>>;
type SetRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;
type SetRequiredOmit<T, K extends keyof T> = Pick<T, K> & Required<Omit<T, K>>;

获取所有可选属性

// 拿到 所有的 可选属性
type GetPartial<T> = {
  [K in keyof T as T[K] extends Required<T>[K] ? never : K]: T[K]
}

type OptionalKeys<T> = {
  [P in keyof T]: Pick<T, P> extends Required<Pick<T, P>> ? never : P
}[keyof T]


const article2: GetPartial<CreateArticle<string>>  = {
  id: '1',
  desc: '2'
}

空对象类型

type PropertyKey = string | number | symbol
type EmptyObject = {
  [K in PropertyKey]: never 
}

// 测试用例
const shouldPass: EmptyObject = {}; // 可以正常赋值
const shouldFail: EmptyObject = { // 将出现编译错误
  prop: "TS"
}

type SomeType =  {
  prop: string
}

type Exclusive<T1, T2 extends T1> = {
  [K in keyof T2]: K extends keyof T1 ? T2[K] : never 
}

// 更改以下函数的类型定义,让它的参数只允许严格SomeType类型的值
function takeSomeTypeOnly<T extends SomeType>(x: Exclusive<SomeType, T>) { return x }

// 测试用例:
const x = { prop: 'a' };
takeSomeTypeOnly(x) // 可以正常调用

const y = { prop: 'a', addditionalProp: 'x' };
takeSomeTypeOnly(y) // 将出现编译错误

非空数组

type NonEmptyArray<T> = [T, ...T[]]

type NonEmptyArray<T> = T[] & { 0: T };

type NonEmptyArray = {
	[P in (keyof T[] & 0)]: P extends number ? T | undefined : T[][P]
}

移除readonly

type Foo = {
  readonly a: number;
  readonly b: string;
  readonly c: boolean;
};

type Mutable<T, Keys extends keyof T = keyof T> = {
   -readonly [K in Keys]: T[K]
} & Omit<T, Keys>

const mutableFoo: Mutable<Foo, 'a'> = { a: 1, b: '2', c: true };

mutableFoo.a = 3; // OK
mutableFoo.b = '6'; // Cannot assign to 'b' because it is a read-only property.
mutableFoo.c = '6'; // Cannot assign to 'c' because it is a read-only property.

const mutableFoo2: Mutable<Foo, 'a' | 'b'> = { a: 1, b: '2', c: true };

mutableFoo2.a = 3; // OK
mutableFoo2.b = '6'; // OK
mutableFoo2.c = '6'; // Cannot assign to 'c' because it is a read-only property.

从类型中挑选出符合条件的

interface Example {
  a: string;
  e: number;
  b: string | number;
  c: () => void;
  d: {};
  f: string | number | boolean;
}
// 定义
type ConditionalPick<V, T> = {
  [K in keyof V as V[K] extends T ? K : never]: V[K];
};
// 使用
type StringKeysOnly = ConditionalPick<Example, string | number>;

为函数新增参数

使用 Parameters 和 ReturnType 工具类型

type AppendArgument<F extends (...args: any) => any, A> 
  = (x: A, ...args: Parameters<F>) => ReturnType<F> 

type Fn = (a: number, b: string) => number
type FinalF = AppendArgument<Fn, boolean> 
// (x: boolean, a: number, b: string) => number

使用 infer 方式

type AppendArgument<F, T> = F extends (...args: infer Args) => infer Return ? 
  (x: T, ...args: Args) => Return : never

type Fn = (a: number, b: string) => number
type FinalFn = AppendArgument<Fn, boolean>
// (x: boolean, a: number, b: string) => number

函数参数属性一致

我们希望参数 ab 的类型都是一致的,即 ab 同时为 numberstring 类型。当它们的类型不一致的值,TS 类型检查器能自动提示对应的错误信息

// 使用函数重载
function f(a: string, b: string): string
function f(a: number, b: number): number
function f(a: string | number, b: string | number ): string | number {
  if (typeof a === 'string') {
    return a + ':' + b;
  } else {
    return ((a as number) + (b as number));
  }
}

f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok

// 把参数组合成一种类型// 
const isStrArr = (a: string[] | number[]): a is string[] => typeof a[0] === 'string'

function f(...args: string[] | number[]) {
  if (isStrArr(args)) {
    return args[0] + ':' + args[1];
  } else {
    return args[0] + args[1];
  }
}

f(2, 3); // Ok
f(1, 'a'); // Error
f('a', 2); // Error
f('a', 'b') // Ok

比较类型相等

type IsEqual<T, U> =
	(<G>() => G extends T ? 1 : 2) extends
	(<G>() => G extends U ? 1 : 2)
		? true
		: false;

//test case, it is special
type X=IsEqual<{x:any}, {x:number}>;   // false

type IsEqual<A, B> = A extends B ? (B extends A ? true : false) : false;

// 测试用例
type E0 = IsEqual<1, 2>; // false
type E1 = IsEqual<{ a: 1 }, { a: 1 }>; // true
type E2 = IsEqual<[1], []>; // false

类型包含关系

用于判断指定的类型 E 是否包含在 T 数组类型中

type Includes<T extends Array<any>, E> = T extends [infer A, ...infer B]
  ? IsEqual<A, E> extends true
    ? true
    : Includes<B, E>
  : false;

type I0 = Includes<[], 1>; // false
type I1 = Includes<[2, 2, 3, 1], 2>; // true
type I2 = Includes<[2, 3, 3, 1], 1>; // true

合并类型

实现一个 Merge 工具类型,用于把两个类型合并成一个新的类型。第二种类型(SecondType)的 Keys 将会覆盖第一种类型(FirstType)的 Keys。具体的使用示例如下所示:

// 1
type Merge<FirstType, SecondType> = Omit<FirstType, keyof SecondType> & SecondType

// 2
type Merge <FirstType, SecondType> = {
    [K in keyof (FirstType & SecondType)]:
    K extends keyof SecondType ?
    SecondType[K] :
    K extends keyof FirstType ?
    FirstType[K] :
    never
}

// 3
type ExtractType<FirstType, SecondType> = {
    [K in Extract<keyof FirstType, keyof SecondType>]: SecondType[K]
};

type Merge <FirstType, SecondType> = Omit<FirstType, keyof SecondType> & ExtractType<FirstType, SecondType>;
  • 27
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Typescript入门教程是阮一峰编写的一份关于Typescript编程语言的教程,旨在帮助初学者快速掌握这种新兴的编程语言。 Typescript是一种由微软公司研发的开源编程语言,它是JavaScript的超集,为JavaScript添加了静态类型检查、类、接口和其他面向对象编程的特性,可以避免一些常见的错误。 阮一峰的Typescript入门教程通过简单易懂的语言,详细的示例和练习,帮助读者逐步掌握Typescript的语法、开发工具、类型注解、类、接口、泛型等核心概念和技术,同时提供了一些实用的开发方法和技巧。 此外,Typescript入门教程也包括了常见的开发场景,如React、Node.js、Angular等的使用,方便读者将所学知识应用到实际项目中。 总的来说,阮一峰的Typescript入门教程是一份优秀的教程,对于想要学习Typescript的初学者来说是一本不容错过的参考资料。通过这份教程,读者可以快速掌握Typescript编程语言,为进一步开发前端应用打下良好的基础。 ### 回答2: 《TypeScript 入门教程》是阮一峰所编写的一本 TypeScript 的自学教程教程分为多个章节,每个章节都有一个完整的代码示例供读者参考。整本教材以易于理解和实践性为重点,让初学者能够很快上手 TypeScript 编程。 首先,阮一峰讲解了 TypeScript 的基本概念和语法,包括数据类型、变量声明、函数等等常见的编程语法。其次,他向读者展示如何使用 TypeScript 编写面向对象程序,包括类和继承机制以及接口和泛型等高级特性。最后,教程使用了多个实际的案例来说明 TypeScript 的实战应用,比如如何使用 React 和 Angular 框架开发 Web 应用程序。 整个教程的难度设置适中,既方便初学者学习掌握,同时也考虑到了经验丰富的开发者学习需要。此外,教程的核心内容都有相关的实验代码示例供大家进行实践练习,可以更好的巩固知识点,让读者能够迅速掌握 TypeScript 的所有语言特性。 总的来说,《TypeScript 入门教程》是一本实用的教学材料,任何对 TypeScript 有兴趣的人都可以通过本书快速学习并掌握 TypeScript 的基本语法和用法。对于希望深入了解 TypeScript 的读者来说,本书也为各位提供了很好的参考和实践基础。 ### 回答3: 《TypeScript入门教程》是针对那些想要学习TypeScript的开发人员而编写的一本指南。这本书由知名技术博主阮一峰所写,以简洁明了的语言和大量实例讲解了TypeScript的基本知识和应用。整本书分为七章,从入门到深入,全面涵盖了TypeScript语言的各个方面,从基本类型、函数、类、模块等最基础的概念讲起,到高级的装饰器、泛型、交叉类型、联合类型等进一步讲解,最后是实战项目,让读者了解如何在实际项目中使用TypeScript。 该书的内容涵盖了TypeScript语法的方方面面,从入门到高级,读者可以通过详细的讲解,了解到如何使用TypeScript进行类型检查、接口、类、泛型等编程概念;此外,书中也穿插了一些实例,帮助读者更好地理解概念,并且提供了一些实用的编程技巧,加强了读者的实际操作能力。 《TypeScript入门教程》是一本极为优秀的TypeScript学习指南,无论是初学者还是进阶学习者,都可以从中获得实用的知识和技能。阮一峰在编写该书时,考虑了读者的需求,以易懂、实用、全面为出发点,值得交的书籍。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值