2022 最新TypeScript入门学习笔记

TypeScript的概念

TypeScript是什么?

TypeScript是JavaScript类型的超集,它可以编译成纯JavaScript。
TypeScript可以在任何浏览器、任何计算机和任何操作系统上运行,并且是开源的。

Typescript的优缺点

  • 优点
  1. 在代码书写和编译阶段进行错误提示
  2. 借助编辑器可以扩展更多功能,包括代码提示补全、接口提示等
  3. 可以直接编译成js文件
  • 缺点
  1. 增加了学习成本,其中涉及到很多非前端开发工程师所理解的知识点
  2. 增加了开发的时间成本,因为相比js要定义很多类型。

将TypeScript安装到全局

yarn add typescript -g
yarn add ts-node -g // 用于执行ts文件(会进行语法检查,速度慢)
yarn add esno -g // 用于执行ts文件(不进行语法检查,速度快)

在线编写TypeScript

playcode 比较推荐,编译ts速度极快
Playground 官方提供的在线ts工具,更加利于分享

一、TypeScript中的基本数据类型

1.1 string

let str1: string = 'hello'; // 成功

let str2: string = new String(); // 失败
// str2报错原因是因为new String返回的是一个对象,而非一个字符串;
let str3: String = new String(); // 成功

1.2 number

const num1: number = 1; // 支持整数
const num2: number = 0xffff; // 支持16禁止
const num3: number = 0b010101; // 支持二进制
const num4: number = 0o001; // 支持八进制
const num5: number = 1.001; // 支持小数
const num6: number = NaN; // 支持NaN
const num7: number = Infinity; // 支持Infinity

1.3 boolean

let boo1: boolean = false;
let boo2: boolean = true;
let boo3: boolean = Boolean();

1.4 undefined 和 null

let und: undefined = undefined;
let nul: null = null;

因为null和undefined是所以类型的子集,所以其他类型的变量进行初始化时,也可以使用null或undefined进行赋值

let str3: string = null;
let num8: number = null;

1.5 void (空值)

一般表示一个没有返回值的函数,或表示一个变量值为undefined

function foo(name: string): void {
  console.log(name);
}
foo('张三');
let void1: void = undefined;

1.6 any(任意值)

  1. any类型的变量可以被随便赋值和改变
let a1: any = 1; // 初始值为number类型
a1 = '1'; // 更改为字符串 
  1. 初始值未设置类型时,默认类型为any
let a2;
a2 = 1;
a2 = '1';
a2 = false;
a2 = {};

1.7 unknown(未知类型)

let un1: unknown = 'ABC';
un1.toLowerCase(); // (报错) 尽管结果为字符串类型 ,但是ts仍然无法推导unknown类型的变量
// 可以配合类型检查 帮助ts分析变量的类型
if (typeof un1 === 'string') un1.toLowerCase(); // 成功的

// 也可以使用as进行强制类型转换
(un1 as string).toLowerCase()

1.8 bigint类型 (大数型 es2020新增)

let big1: bigint = BigInt(1000);
let big2: bigint = big1 + 1000n; // bigint参与计算
let big3: bigint = big1 + big2; // bigint参与计算
console.log(big3);

1.9 symbol

let sym1: symbol = Symbol();

二、TypeScript中的对象类型

2.1 object

object可以表示一个变量的类型为对象,数组和函数等非原始类型数据

let o1:object = {};
o1 = []; // 正确的
o1 = () => {}; // 正确的
o1 = ''; // 错误的
o1 = 1; // 错误的

2.2 Object

Object用于表示任何原始类型和非原始类型数据,严格模式下不能赋值为null和undefined

let o2: Object = {};
o2 = []; // 正确的
o2 = () => {}; // 正确的
o2 = ''; // 正确的
o2 = 1; // 正确的
o2 = undefined; // 严格模式下错误
o2 = null; // 严格模式下错误

2.3 {}

Object 和 {} 使用一致,都表示原始类和非原始值数据严格模式下不能赋值为null和undefined

let o3: {} = {};
o3 = []; // 正确的
o3 = () => {}; // 正确的
o3 = ''; // 正确的
o3 = 1; // 正确的
o3 = undefined; // 严格模式下错误
o3 = null; // 严格模式下错误

三、类型操作符

3.1 类型声明

类型声明也叫类型别名,在 TypeScript 一般使用 type 对类型进行命名

// 使用type声明一个类型为string的别名
type Address = string;
const address:Address = '中国上海' ;

3.2 联合类型

使用 type 进行声明类型, 并且使用 | 进行联合类型

// 在项目中 我们一般使用 '男' 或 1 来表示男性
type Sex = string | number;
 
let a: Sex = 1;
a = '男'; // 使用联合类型声明后,a变量的值可以更改为number 或 string 类型,但是不能为其他类型
a = false; // 错误的赋值

3.3 交叉类型

交叉类型表示,多个类型共同组合叠加成一个新的类型,该类型包含所有交叉类型的特性

type TypeName = {
  name: string;
};
type TypeAge = {
  age: number;
};
// 进行类型联合
type User = TypeName & TypeAge;
let user1: User = {
  name: '小明',
  age: 12,
};

// 也可以直接在声明变量时进行联合
let user2: TypeName & TypeAge = {
  name: '小明',
  age: 12,
};

3.4 类型推导

当你没有给变量指定一个类型时, TypeScript 会帮你根据变量的结果推测出一个类型;

let a = '1'; // 此时ts会自动推导出a的类型为字符串
a = 1; // (报错) 不能将类型“number”分配给类型“string”。

3.5 类型断言

类型断言是为了帮助 TypeScript 给出更加具体的类型,可以使用 as 关键字按照自己断言的类型帮助ts通过编译

// 将变量类型指定为unknown(不确定)类型
let b: unknown = 'ABC';

b.toLowerCase(); // 报错 “b”的类型为“未知”
(b as string).toLowerCase(); // 将b变量的类型 断言为string类型即可通过编译

3.6 字面量类型

TypeScript 中,变量的结果也可以作为变量的类型来使用。常用的字面量类型为number、string、boolean

let c1: 10 = 10; // 数字字面量类型
let c2: false = false; // boolean字面量类型
let c3: 'hello' = 'hello'; // 字符串字面量类型

四、数组和元祖(Array、Tuple)

4.1 数组推导

let arr1 = [1, 2, 3]; // 推导为number组成的数组 number[]
arr1.push('4'); // 报错
arr1.push(4); // 成功

let arr2 = [1, '2']; // 推导为number[] 或 string[]
arr2.push(3, '4'); // 成功

4.2 元祖类型

元祖表示一个数量固定并且类型固定的数组,它是一种特殊的数组

let arr3: [number, string, boolean]; // 表示元祖长度为3,类型分别为number string和boolean
arr3 = [1, '2', true]; // 成功
arr3 = [-1, '', false]; // 成功

console.log(arr3[3]); // 报错 (不能读取超过长度限制的元素)

arr3.push(1, 2, 3, 4, 5); // 可以通过push或unshift添加元素
arr3.unshift(0, 1, 2); // 可以通过push或unshift添加元素

五、枚举类型

在工作中,经常会遇到某一个变量仅仅存在几个固定的取值,如星期一到星期日,只有7种取值,那么就可以使用枚举,将变量和值列举成对应关系。

5.1 整数枚举

// 整数枚举会从0开始依次递增
enum EnumCars1 {
  Monday,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday
}

console.log(EnumCars1.Monday); // 结果为0
console.log(EnumCars1.Tuesday); // 结果为1
console.log(EnumCars1.Wednesday); // 结果为2

console.log(EnumCars1[0]); // 结果为 Monday
console.log(EnumCars1[1]); // 结果为 Tuesday
console.log(EnumCars1[2]); // 结果为 Wednesday

5.2 整数枚举控制起始数

// 初始的枚举值手动设为100,后面的依次进行递增
enum EnumCars2 {
  Monday = 100,
  Tuesday,
  Wednesday,
  Thursday,
  Friday,
  Saturday,
  Sunday
}
console.log(EnumCars2.Monday); // 100
console.log(EnumCars2.Tuesday); // 101
console.log(EnumCars2.Wednesday); // 102
console.log(EnumCars2[100]); // Monday

// 从某个位置改变数值,后面的变量沿着这个值继续递增
enum EnumCars3 {
  Monday = 100,
  Tuesday,
  Wednesday,
  Thursday = 1000,
  Friday,
  Saturday,
  Sunday,
}

console.log(EnumCars3.Monday); // 100
console.log(EnumCars3.Tuesday); // 101
console.log(EnumCars3.Wednesday); // 102
console.log(EnumCars3.Thursday); // 1000
console.log(EnumCars3.Friday); // 1001
console.log(EnumCars3.Saturday); // 1002
console.log(EnumCars3.Sunday); // 1003
console.log(EnumCars3[1003]); // Sunday

5.3 常量整数枚举

和普通枚举的区别是,普通枚举可以使用整数访问枚举值,但是常数枚举只能使用string访问,不能使用整数进行访问

const enum EnumCars4 {
  Monday,
  Tuesday,
  Wednesday,
}
console.log(EnumCars4.Monday); // 0
console.log(EnumCars4.Monday); // 1
console.log(EnumCars4.Monday); // 2
console.log(EnumCars4[0]); // 报错 (只有使用字符串文本才能访问常数枚举成员。)

5.4 常量字符串枚举

const enum EnumCars5 {
  Monday = '星期一',
  Tuesday = '星期二',
  Wednesday = '星期三',
}

console.log(EnumCars5.Monday); // 星期一
console.log(EnumCars5.Tuesday); // 星期二
console.log(EnumCars5.Wednesday); // 星期三

六、TypeScript函数

6.1 函数的定义和参数

// 普通命名函数 参数name为string类型
function foo1(name: string) {
  return '姓名为:' + name;
}

// 匿名函数 参数age为number类型
const foo2 = (age: number) => '年龄为:' + age;

6.2 函数的返回值类型

不写函数的返回值类型时,默认为void,表示没有返回值或返回值为undefined,也可以指定函数的返回值类型

// 指定回类型是string类型
function foo3(name: string): string {
  return '姓名为:' + name;
}
function foo4(name: string): string {
  return 123; // 返回number类型则会报错
}

6.3 函数的默认参数和可选参数

默认参数在定义参数时使用 =进行赋值,可选参数使用 ?进行标注

function foo4(name: string = '小明', age?: number): string {
  return `姓名是:${name},年龄是: ${age || '未知'}`;
}

console.log(foo4()); // 姓名是:小明,年龄是: 未知
console.log(foo4('小红', 20)); // 姓名是:小红,年龄是: 20

6.4 不固定参数

当不确定函数的参数个数时,可以使用剩余 ... 参数进行处理

// 所有参数组成的数组,不考虑参数的类型
function foo5(...args: any[]): void {
  console.log(args);
}
// 除第一位参数,剩余参数组成的数组,参数类型必须全部为number
function foo6(a: number, ...args: number[]): void {
  console.log(args);
}

foo5(1, 2, 3, 4, 5); // 打印结果为: [ 1, 2, 3, 4, 5 ]
foo6(1, 2, 3, 4, 5); // 打印结果为: [  2, 3, 4, 5 ]

6.5 函数内部的this

type TypeThis = {
  say(): void;
};
// 将函数的this指定为 TypeThis 类型,内部就可以通过this调用TypeThis上面的say方法
function foo7(this: TypeThis) {
  this.say();
}
const bar: TypeThis = {
  say() {
    return 'say';
  },
};
// 调用foo函数,并将this指向 bar 对象
const result = foo7.call(bar);
console.log(result); // 'say'

6.6 函数重载

函数重载是多个函数声明,一个函数实现,最终实现的函数需要满足上面所有声明的约束

function foo8(input: string): string;
function foo8(input: number): string;
function foo8(input: number | string): string {
  if (typeof input === 'string') return `输入的值是字符串类型`;
  else return `输入的值是数字类型`;
}

七、TypeScript类

7.1 类的成员和方法

TypeScript 中,通过class创建一个类,可以在类中定义成员属性和方法

class Person {
  name: string;
  constructor(name:string) {
    this.name = name;
  }
  getName(): string {
    return this.name;
  }
}

const p1 = new Person('randian');
console.log(p1.getName);

7.2 类的修饰符

TypeScript 中,类中的属性和方法可以使用类的修饰符进行修饰,常用的修饰符有public、private、protected。

  • public表示公开属性,可以在任何地方进行访问,也是类的默认修饰符
  • private表示私有属性,只能在类的内部使用,不能在子类和实例身上
  • protected表示属性和方法是被保护的,可以在类的内部和子类的内部进行访问
class Person1 {
  public name: string; 
  private age: number; 
  protected sex: string;
  constructor(name:string) {
    this.name = name;
  }
  public getName(): string {
    return this.name;
  }
  private getAge() {
    return this.age;
  }
  protected getSex() {
    return this.sex;
  }
}
const p2 = new Person1('randian');
console.log(p2.getName()); // 成功 randian
console.log(p2.getAge()); // 报错 属性“getAge”为私有属性,只能在类“Person1”中访问
console.log(p2.getSex()); // 报错 属性“getSex”受保护,只能在类“Person1”及其子类中访问。

7.3 extends 继承

TypeScript 类中,继承使用extends关键字,在子类的构造器内,通过super调用父类的构造器方法,将需要继承的属性传入super

// 父类
class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}
// 子类通过extends继承父类
class Dog extends Animal {
  action: string;
  constructor(name: string, action: string) {
    super(name);
    this.action = action;
  }

}

const dog = new Dog('小狗', '啃骨头');

7.4 只读属性 readonly

TypeScript 中, 使用readonly对属性限制只读

class Animal1 {
  readonly name: string; // 声明name是只读属性
  constructor(name: string) {
    this.name = name;
  }
  getName() {
    return this.name; // 可以进行访问
  }
  setName(_name: string) {
    this.name = _name; // 报错: 无法为“name”赋值,因为它是只读属性
  }
}

7.5 存取器 get、set

TypeScript 中, 可以使用 getset函数对属性进行读取和写入,其原理是Es5中的Object.definedPrototype

class Animal2 {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  // 通过get获取属性
  get value() {
    console.log('get方法被执行了');
    return this.name;
  }
  // 通过set设置属性
  set value(value: string) {
    console.log('set方法被执行了');
    this.name = value;
  }
}

const dog1 = new Animal2('旺财');
console.log(dog1.value); // 旺财
dog1.value = '小花';
console.log(dog1.value); // 小花

7.6 抽象类 abstract

TypeScript 中,存在抽象类的概念,抽象类有以下几个特征

  • 抽象类不能直接实例化,抽象类一般用作普通类的基类,被普通类继承使用;
  • 抽象类内部可以有自己的方法和抽象方法(通过abstract修饰的方法),抽象方法只能定义,不能实现,必须在子类中实现;
abstract class Animal3 {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
  // 抽象类的普通方法
  say() {
    console.log('say方法执行le');
  }
  // 抽象方法只能在抽象类中定义,不能直接实现
  abstract foo(): void;
}

const dog2 = new Animal3('旺财'); // 报错: 无法创建抽象类的实例

// 声明一个类继承抽象类
class Dog3 extends Animal3 {
  constructor(name: string) {
    super(name);
  }
  // 必须实现抽象类中的抽象方法
  foo(): void {
    console.log('foo方法执行了');
  }
}

const dog3 = new Dog3('旺财');

dog3.foo(); // foo方法执行了
dog3.say(); // say方法执行了

八、接口

8.1 接口的定义

TypeScript 中,通过 interface 关键字来定义接口

interface Users {
  name: string;
  address: string;
  age?: number; // 可选参数
  readonly sex: string; // 只读属性
}
// 使用在对象身上
const user: Users = {
  name: 'randian',
  address: '上海',
  sex: '男',
};
user.sex = '女'; // 报错: 无法为“sex”赋值,因为它是只读属性。

// 在类上使用
// 使用implements使用接口
class UserInfo implements Users {
  name: string;
  address: string;
  readonly sex: string;
  constructor(name: string, address: string, sex: string) {
    this.name = name;
    this.address = address;
    this.sex = sex;
  }
}

8.2 接口的任意类型

当我们不确定一个接口都有哪些参数时,可以使用[key: string]表示这个接口接受任意参数;其中key可以是任意字符。

interface Users {
  [key: string]: any;
}

8.3 接口的继承

TypeScript 中,接口和接口之间可以使用extends进行继承

interface Users {
  name: string;
  address: string;
  age?: number; // 可选参数
  readonly sex: string; // 只读属性
}
// 该接口继承于Users接口.并在自身新增了两个属性
interface Details extends Users {
  message: string;
  ater(): void;
}
const des: Details = {
  name: 'randian',
  address: '上海',
  sex: '男',
  message: '成功的消息',
  alert: () => {
    console.log(des.message);
  },
};

8.4 多接口实现

interface A {
  name: string;
}
interface B {
  age: number;
}
interface C {
  sex: string;
}
class Person2 implements A, B, C {
  name: string;
  age: number;
  sex: string;
}

8.5 函数类型接口

interface GetterFn {
  (message: string, ...args: any[]): boolean;
}
const fn: GetterFn = (message: string, ...args: any[]) => {
  return args.length > 2;
};
console.log(fn('hello', 1, 2)); // false
console.log(fn('hello', 1, 2, 3)); // true

九、泛型

泛型是指在定义类,函数,接口时,不予先限制类型,在使用时再指定类型

9.1 泛型的实例

// 使用T和V作为类型的占位符,在使用时传入对应的类型(T和V可以是任意字符)
class Person<T, V> {
  private name: T;
  private age: V;
  constructor(_name: T, _age: V) {
    this.name = _name;
    this.age = _age;
  }
  getName(): T {
    return this.name;
  }
  getAge(): V {
    return this.age;
  }
}
// 使用时替换T,V对应的类型
const p1 = new Person<string, number>('randian', 18);

9.2 泛型约束

// 在使用泛型变量时,因为预先不知道变量的类型,导致一些api在使用时会发生错误,这个时候就需要使用extends进行约束
function getLength<T extends any[]>(arr: T): number {
  return arr.length;
}

console.log( getLength<number[]>([1,2,3,4]) );

9.3 泛型参数的默认类型

class Users<T = string> {
  private id: T;
  constructor(_id: T) {
    this.id = _id;
  }
  getId(): T {
    return this.id;
  }
}

const user1 = new Users('1'); // 默认类型为string
const user2 = new Users<number>(1); // 传入number的id

十、高级特性

10.1 in 操作符

遍历并集的每一个元素,将字面量类型的值转为属性

type Key = 'k1' | 'k2' | 'k3';
type MyMap = {
  [K in Key]: number;
};
const map: MyMap = {
  k1: 1,
  k2: 2,
  k3: 3,
};

10.2 keyof 操作符

将object中的key转为字面量类型

type Users = {
  name: string;
  age: number;
  sex: string;
};

type userKeys = keyof Users;

let k1: userKeys;
k1 = 'name';
k1 = 'age';
k1 = 'sex';

10.3 keyof 和 in 的结合

type Users = {
  name: string;
  age: number;
  sex: string;
};
// 创建一个新的类型,将Users里面的参数变成可选的
type OptionalUsers = {
  [K in keyof Users]?: Users[K];
};
// 参数是必填的
const o1: Users = {
  name: 'randian',
  sex: '男',
  age: 18,
};
// 参数是可选的
const o2: OptionalUsers = {
  name: 'randian',
};

10.4 条件类型 - 三元运算符

class UserMan {
  sex: string = '男';
}
class UserWoMan {
  sex: string = '女';
}
// extends在编译时会进行条件判断
type ResultUser<T extends boolean> = T extends true ? UserMan : UserWoMan;

function getUserInfo<T extends boolean>(isMan: T): ResultUser<T> {
  return isMan ? new UserMan() : new UserWoMan();
}
// 根据boolean返回具体的实现
const user1 = getUserInfo(false);
const user2 = getUserInfo(true);

10.5 条件类型 - infer

  • infer只能跟在extends后使用
type ElemType<T> = T extends (infer U)[] ? U : never;

type A = ElemType<number[]>; // number
type B = ElemType<string[]>; // string
type C = ElemType<string>; // never

// 使用infer返回数组的第一位元素
type ArrayOne<T> = T extends [infer U, ...any[]] ? U : never;
type D = ArrayOne<[1, 2, 3]>; // 1
type E = ArrayOne<[false, '1', true]>; // false

// 获取对象中的类型
type GetValueType<T> = T extends { name: infer V; age: infer U } ? [V, U] : [];
type F = GetValueType<Users>;

// 获取函数的返回值类型
type Fns = (name: string, age: number) => string;
type GetFnReturn<T> = T extends (name: any, age: any) => infer D ? D : never;
type G = GetFnReturn<Fns>;

10.6 映射类型 - Pick

将集合中的一部分映射到新的集合中

type User2 = {
  name: string;
  age: number;
  sex: string;
};

type PickUser = Pick<User2, 'name' | 'age'>;
const u1: PickUser = {
  name: 'randianx',
  age: 18,
};

10.7 映射类型 - Partial

将集合中的属性变为可选属性

type User2 = {
  name: string;
  age: number;
  sex: string;
};
type PartialUset = Partial<User2>;
const u2: PartialUset = {
  name: 'randian',
};

10.8 映射类型 - Record

规定属性和值的类型返回一个集合

type User3 = Record<string, number>;
const u3: User3 = {
  小明: 1,
  小张: 2,
};

10.9 映射类型 - Readonly

将集合内的属性变为只读的

type User2 = {
  name: string;
  age: number;
  sex: string;
};
type ReadonlyUser = Readonly<User2>;
const u4: ReadonlyUser = {
  name: 'randian',
  age: 18,
  sex: '男',
};
u4.sex = '女'; // 报错: 无法为“sex”赋值,因为它是只读属性。

10.10 映射类型 - Omit

从集合中剔除某一个或多个属性

type User2 = {
  name: string;
  age: number;
  sex: string;
};
type OmitUser = Omit<User2, 'age' | 'sex'>;
const u5: OmitUser = {
  name: 'randian',
};

10.11 映射类型 - Required

将集合中的属性转为必填项

type User3 = {
  name?: string;
  age?: number;
  sex: string;
};
type RequiredUser = Required<User3>;
const u6: RequiredUser = {
  name: 'randian',
  age: 18,
  sex: '男',
};

10.12 映射类型 - Extract

取两个集合之间的交集作为一个新的集合

// 声明一个圆的类型
type Circular = {
  radius: number;
  center: [number, number];
  perimeter: number;
  area: number;
};
// 声明一个正方形的类型
type Square = {
  width: number;
  height: number;
  perimeter: number;
  area: number;
};
// 圆形和正方形都有周长和面积
type CS = Extract<keyof Circular, keyof Square>; // "perimeter" | "area"

type A = number | string | boolean | Symbol;
type B = number | string | Symbol;
type AB = Extract<A, B>; //  number | string  | Symbol

十一、高级类型-类型体操

type User = {
  id: number | string;
  name?: string;
  age: number;
  sex: string;
  address?: string;
  children?: User[];
};

11.1 实现Required - 将集合中的属性变为必填项

type MyRequired<T> = {
  [K in keyof T]-?: T[K];
};
type RequiredUser = MyRequired<User>;
const u1: RequiredUser = {
  id: 1,
  name: 'randian',
  age: 18,
  sex: '男',
  address: '上海',
  children: [],
};

11.2 实现Record - 根据属性和值的类型返回一个集合

type MyRecord<K extends keyof any, T> = {
  [P in K]: T;
};
const u2: MyRecord<number, boolean> = {
  1: false,
  '2': true,
};

11.3 实现Partial - 将集合中的属性都变成可选属性

type MyPartial<T> = {
  [K in keyof T]?: T[K] | undefined;
};
const u3: MyPartial<User> = {
  name: 'randian',
};

11.4 实现Pick - 将集合中的部分属性返回到一个新的集合中

type MyPick<T, K extends keyof T> = {
  [P in K]: T[P];
};
const u4: MyPick<User, 'sex' | 'age' | 'id'> = {
  age: 18,
  sex: '男',
  id: 1,
};

11.5 实现Omit - 排除掉集合中的部分属性返回到一个新的集合

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

const u5: MyOmit<User, 'sex' | 'age' | 'id'> = {
  name: 'randian',
  address: '上海',
  children: [],
};

11.6 实现Readonly - 将集合中的属性变为可读的

type MyReadonly<T> = {
  readonly [K in keyof T]: T[K];
};
const u6: MyReadonly<User> = {
  id: 1,
  age: 18,
  sex: '男',
};
// u6.age = 19; // 报错: 无法为“age”赋值,因为它是只读属性。
export {};

11.7 实现Extract -取两个集合的交集作为一个新的集合

type MyExtract<T, K> = T extends K ? T : never;

type Circular = {
  radius: number;
  center: [number, number];
  perimeter: number;
  area: number;
};
type Square = {
  width: number;
  height: number;
  perimeter: number;
  area: number;
};
// 圆形和正方形都有周长和面积
type CS = MyExtract<keyof Circular, keyof Square>; // "perimeter" | "area"

type A = number | string | boolean | Symbol;
type B = number | string | Symbol;
type AB = MyExtract<A, B>; //  number | string  | Symbol

11.8 实现NonNullable - 从集合中剔除null和undefined的值

type MyNonNullable<T> = T extends null | undefined ? never : T;
type Ext = string | null | undefined | boolean;
type NotNullTpye = MyNonNullable<Ext>;
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值