typescript学习笔记

1.概述

TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,因此现有的 JavaScript 代码可与 TypeScript 一起工作无需任何修改,TypeScript 通过类型注解提供编译时的静态类型检查。
TypeScript 可处理已有的 JavaScript 代码,并只对其中的 TypeScript 代码进行编译。

[图片上传失败...(image-daff3e-1659492070783)]

2.快速上手

1.安装

yarn add typescript --dev

[图片上传失败...(image-67984d-1659492070783)]

2.使用【tsc】命令检测测试是否安装成功

[图片上传失败...(image-86fbb-1659492070783)]

3.写个栗子

// 01.first.ts
// 编译
const fn = str => `hello , ${str}`;
fn(`ts`);

// 类型系统
const num:number = 12;
console.log(num);

执行tsc命令,编译01.first.ts
[图片上传失败...(image-812c5d-1659492070784)]
运行结果
[图片上传失败...(image-f272ee-1659492070784)]

3.ts配置文件

1.运行命令 ,生成配置文件 tsconfig.json

yarn tsc --init

[图片上传失败...(image-fa59a9-1659492070784)]

2.compilerOptions 编译器选项

{
  "compilerOptions": {
    "target": "es2016",  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
    "outDir": "dist",  /* Specify an output folder for all emitted files. */
        "rootDir": "src",   /* Specify the root folder within your source files. */
    "sourceMap": true,  /* Create source map files for emitted JavaScript files. */


  }
}
  • target : 设置编译后的javascript所采用的ecmaScript规范
  • module : 输出的代码以什么方式模块化
  • outDir :编译后的文件输出的路径
  • rootDir : 源代码存放的文件夹
  • sourceMap : 源代码信息文件 方便调试
  • lib : 编译时引入的 ES 功能库

4.ts作用域问题

[图片上传失败...(image-20e22a-1659492070784)]
ts 在不同的文件中去使用相同名称的变量,变量名重复时,ts会检查错误,变量名重复声明。原因是都在全局的作用域下声明的

解决方式 :

  1. 使用自执行函数包裹
    2. 使用export {}
const num : number = 0;

export {}; // 将文件作为一个模块 使当前文件的成员在本模块的局部作用域中使用

5.ts 基础类型

1.string number boolean any void never

// 原始数据类型
// string 字符串
const str:string = 'string';

// 数值 number
let num : number = 0;

// 布尔 boolean
const flag : boolean = true;

// null 和 undefined 
const a : null = null;
const b : undefined  = undefined;

// 任意值 any 
const d : any = 1;
d.value = 'aaa';

// 空值 void 表示一个对象没有任何类型 一般用法是函数没有返回值 就是void
const fn = ():void=>{
    console.log(`some thing`)
}

// never 永远不存在的值 一般用法 : 会抛出异常的函数 或者 函数永远达不到终点,不会有返回值
const neverFn = ():never=>{
    throw new Error('error')
}
const loopFn = ():never =>{
    while(true){}
}


export {}; // 将文件作为一个模块 使当前文件的成员在本模块的局部作用域中使用

2.数组 元组 枚举

// 数组类型的两种写法
// 1 在元素类型后面接上[],表示由此类型元素组成的一个数组
const arr: number[] = [1, 3, 4];
// 2 数组泛型,Array<元素类型>
const arr2: Array = [1, 3, 4];

// 元组类型 tuple 表示一个已知元素数量和类型的数组,各元素的类型不必相同
const ary: [number, string] = [2, "mingming"];
// const ary2:[number,number] = [2, 2,'mingming']; 对应的值的类型 和 数量都需要保持一致

// 枚举类型 enum
enum STATUS {
  ONLINE,
  OFFLINE,
  ALARM,
}
console.log(STATUS[0]); // 'ONLINE'
const status: STATUS = STATUS.ONLINE;
console.log(status); // 0

// 枚举默认是从0开始编号的 ,也可以手动指定成员的编号值
enum FOOD {
    RICE = 1,
    NOODLE = 4,
    GRUEL = 5 
}

console.log(FOOD.RICE)  // 1

// 枚举类型提供的一个便利是你可以由枚举的值得到它的名字。 
enum DEVICE {
    '塔机' = 14,
    '升降机' = 15,
    '架桥机' = 17
}

console.log(DEVICE[14]); // '塔机'

export {};

6.类型断言 assertion

使用情景:当你知道一个实体具有比它现有类型更确切的类型。
通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。

两种写法

  1. 尖括号< >
  2. as 关键字
// 例如 后端返回的数据 比ts更清楚数据的类型 断言不会对数据进行检查和转换

const responseData : any = `some data`;
const str:string = responseData as string;

const len:number = (responseData).length;

7.接口 interface

  • TypeScript的核心原则之一是对值所具有的结构进行类型检查
  • 接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约
  • 类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以

可选属性

可选属性的接口,是在可选属性名字定义的后面加一个?符号

只读属性

只能在对象刚刚创建的时候修改其值。 可以在属性名前用readonly来指定只读属性

// interface
interface IParams {
    deviceId : number;
    status ?: number;
    readonly id : string;
}

const getData = (params:IParams)=>{
    console.log(params.deviceId);
    // params.id = 'id_2';  // 会提示语法错误,因为id是只读属性
}

getData({deviceId:1,id:'id_1'})

ReadOnlyArray 类型

与Array相似,只是把所有可变方法去掉了,因此可以确保数组创建后再也不能被修改

// ReadOnlyArray 
let n : number[] = [1,3,4];
const r : ReadonlyArray = [1,3,4];
// r[1] = 4; // 不能被修改
// n = r; // 将readonly对象赋值给其他变量也会报错

n = r as number[]; // 使用类型断言 可以赋值


export {}

字符串索引签名

如果我们不确定接口的属性内容,调用的时候需要传入一些额外属性,有两种方式

// 方法一:
getData({deviceId:1,id:'id_1',name:'other'} as IParams);

// 方法二:在定义接口的时候 采用字符串索引签名 注意:当你声明一个索引签名时,所有明确的成员都必须符合索引签名

interface Foo {
  [key: string]: number;
  x: number;
  y: number;
}

interface IParams {
    deviceId : number;
    status ?: number;
    readonly id : string;
    [paramName:string] : any;  // 必须 use any
}

函数类型

interface IFn {
    (name:string,age:number):string
}

const fn2 : IFn = (name:string,age:number)=>{
    return `his name is ${name} , his age is ${age}`;
} 

接口的继承

接口是可以继承的,利用接口的继承属性可以灵活对接口进行复用

interface People {
    firstName :  string
}

interface Student extends People {
    secondName:string
}

interface Teacher extends People {
    fullName:string
}

const xiaoming : Student = {
    firstName:'xiao',
    secondName:'ming'
}

const xiaohong : Teacher ={
    fullName:'xiaohong',
    firstName:'xiao'
}

8.类 class

public公有 private私有 protected受保护 三个修饰符

  • 控制类中成员的访问级别
  • 默认是public
  • protected 和 private 都是仅允许在类的内部访问的。protected与 private的区别是 protected是可以继承的,在子类中也可以访问
// public private protected 公有、私有、受保护的修饰符
class People {
  name: string;
  public age: number;
  protected food : string;

  constructor(name: string, age: number) {
    this.name = name; // 使用name 属性之前 要注解name的类型
    this.age = age;
    this.food = 'rice';
  }
  eat() {
    console.log(`${this.name} is eating`);
  }

  private say() {
    console.log("speaking speaking speaking");
  }

  speak() {
    this.say();
  }
}

const p = new People("xiaoming", 12);
p.eat(); // xiaoming is eating
// p.say(); // Property 'say' is private and only accessible within class 'People'.
p.speak(); // speaking speaking speaking
// p.food; // error:Property 'food' is protected and only accessible within class 'People' and its subclasses.


class A {
    private name : string;
    protected age :number;
    protected sex = 'man';
    constructor(name:string,age:number){
        this.name = name;
        this.age = age
    }
}

class B extends A {
    constructor(){
        super('b',10)
    }
    hello(){
        console.log(`protected sex: ${this.sex}`) 
    }
}

class C {
    private name : string;
    constructor(name:string){
        this.name = name;
    }
}

let a = new A('a',0);
const b = new B();
const c = new C('a');

a = b;
// a = c; // error: Types have separate declarations of a private property 'name'
// B和A 共享了A 的private属性 name , C 虽然有自己的private属性name,但是并不是A的 所以类型不兼容

// console.log(a.sex); // roperty 'sex' is protected and only accessible within class 'A' and its subclasses
b.hello() // protected -> age属性不能在A类外面使用,但是可以继承给A的子类

export {};

implements

  • 实现(implements)是面向对象中的一个重要概念。一般来讲,一个类只能继承自另一个类,有时候不同类之间可以有一些共有的特性,这时候就可以把特性提取成接口(interfaces),用 implements 关键字来实现
interface Ip {
  name: number;
  age?: number;
}

class Person implements Ip {
  name: number;
  age: number;
  constructor(name: number, age: number) {
    this.name = name;
    this.age = age;
  }
}

class Teacher extends Person implements Ip {
  constructor(job:string) {
    super(1,1)
  }
}

const per = new Person(1, 2);

9.函数 function

ts 中的函数

  • 返回值类型是函数类型的必要部分,如果函数没有返回任何值,也必须指定返回值类型为void而不能留空
  • 可选参数 和 默认参数
  • 剩余参数
// ts 中的函数
const fn = (num1: number, num2: number): number => num1 + num2;

function fn2(num1: number, num2: number): number {
  return num1 + num2;
}

// 函数类型的完整写法
const fun: (n1: number, n2: number) => number = (
  num1: number,
  num2: number
): number => num1 + num2;

// 可选参数 和 默认参数
// 可选参数要放在必填参数的后面
// 默认参数的类型要和默认值的类型一致
const article = (name: string, content = "hehehe", subTitle?: string) => {
  console.log(`${name},${subTitle}:${content}`);
};

article("wang", "W");
// article('zhou',123);  // Argument of type '123' is not assignable to parameter of type 'string | undefined'.

// 剩余参数
// 剩余参数会被当做个数不限的可选参数。 可以一个都没有,同样也可以有任意个。
// 编译器创建参数数组,名字是你在省略号(...)后面给定的名字,你可以在函数体内使用这个数组
const surplus = (kind: string, ...args: number[]) => {
  console.log(`${kind} - ${args[3]}`);
};

// 剩余参数定义类型
const surfn: (k: string, ...arg: number[]) => void = surplus;

surplus("cat", 1, 2, 3, 4, 5);

export {};

10.泛型 Generics

  • 定义的时候不指定具体的类型 使用的时候才传递具体的类型
  • 使用泛型来创建可重用的组件

泛型函数

// 泛型函数:函数的最前面有一个类型
function fn(arg: T): T {
  console.log(arg);
  return arg;
}

fn(1);

const fn1 = (arg: Array): Array => {
  return arg.concat(arg);
};
fn1([1]);

泛型接口

// 泛型接口
interface IGenerics {
  (name: T): T;
}

const fn3: IGenerics = (arg: U): U => {
  console.log(111);
  return arg;
};

泛型约束

extends 关键字进行泛型约束

// 泛型约束 操作某类型的一组值,并且知道这组值具有什么样的属性
interface ILength {
  length: number;
}
const fn4 = (args: T): number => {
  return args.length;
};

fn4([8]);

// 泛型约束中使用类型参数
const getValue = (arg:T,key:K)=>{
    return arg[key]
}

const obj = {a:'1',b:'2',c:'3',d:'4'};

// getValue(obj,'e') //error: Argument of type '"e"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.ts(2345)

getValue(obj ,'a')

export {};

11.类型推论

最佳通用类型

  • 当需要从几个表达式中推断类型时候,会使用这些表达式的类型来推断出一个最合适的通用类型
  • 计算通用类型算法会考虑所有的候选类型,并给出一个兼容所有候选类型的类型。
let arr = [0,'str','name','age'];

// arr[4] = null; // error:Type 'null' is not assignable to type 'string | number'.
arr[3] = 7;

上下文类型

  • 如果上下文类型表达式包含了明确的类型信息,上下文的类型被忽略
window.onmousemove = (event)=>{
    // console.log(event.style); // error:Property 'style' does not exist on type 'MouseEvent'.ts(2339)
    console.log(event.target)
}

12.ts高级类型

交叉类型 intersection type

  • 合并的多个接口类型 将多个接口类型求并集得到的结果
// 交叉类型 intersection type
// 合并的多个接口类型 将多个接口类型求并集得到的结果
interface Person {
  name: string;
  age: number;
}

interface Animal {
  eat: () => void;
  run: () => void;
}

const miaomiao: Person & Animal = {
  name: "xx",
  age: 12,
  eat() {},
  run() {},
};

  • 如果合并的多个接口类型中存在同名属性
    • 同名属性兼容: 兼容的同名属性 合并后会是两者类型的子类型 (同 type name = string & '2' // '2' 类型 )
    • 同名属性不兼容: 不兼容的合并后会得到 never类型 (同 type name = string & number //never类型)
interface A {
  a: string;
  b: number;
}

interface C {
  b: string;
  c: boolean;
}

const d: A & C = {
  a: "1",
  b: 2, // Type 'number' is not assignable to type 'never'.
  c: true,
};

联合类型 union types

  • 联合类型表示一个值可以是几种类型之一。 我们用竖线(|)分隔每个类型
  • number | string | boolean表示一个值可以是number,string,或boolean。
  • 联合操作符 | 的优先级低于交叉操作符 &,同样,我们可以通过使用小括弧 () 来调整操作符的优先级
// 联合类型 union types
interface A {
    say:()=>void;
    singing():void;
}

interface B {
    run:()=>void;
    smoking:()=>void;
}

const c : A | B = {
    say():string{
        console.log('say');
        return 'say'
    },
    singing(){
        console.log('singing')
    }
}

// c.run(); // Property 'run' does not exist on type 'A'.ts(2339)

// 混合使用 | & 
type UnionA = 'a' | 'b' | 'c' | 'd';
type UnionB = 'c' | 'd' | 'e' | 'f';
type UnionC = UnionA & UnionB;
const d: UnionC = 'd'

// 优先级
type E = 'a' | 'b' & 'c';
const e:E = 'a'


type P1 = "abc" | string;
type P2 = "abc" | (string & {});
const p1: P1 = "xxx";
const p2: P2 = "xx";

export {};

类型兼容性

  • 类型系统中,如果一个类型的属性更具体,则该类型是子类型。(因为属性更少则说明该类型约束的更宽泛,是父类型)
  • 子类型比父类型更加具体,父类型比子类型更宽泛
interface Animal {
  name: string;
}

interface Dog extends Animal {
  break(): void;
}

let a: Animal;
let d: Dog;

// 可以赋值,子类型更佳具体,可以赋值给更佳宽泛的父类型
a = d;
// 反过来不行
d = a;

类型保护 type guard

  • 类型保护就是一些表达式,它们会在运行时检查以确保在某个作用域里的类型。
interface Bird {
  fly(): void;
  layEggs(): void;
}

interface Fish {
  swim(): void;
  layEggs(): void;
}

function getSmallPet(): Fish | Bird {
  console.log(11);
  return {
    swim() {},
    fly() {},
  } as any;
}

const pet: Fish | Bird = getSmallPet();

// 每一个成员访问都会报错
// if (pet.swim) { // Property 'swim' does not exist on type 'Bird | Fish'.
//     pet.swim();
// }
// else if (pet.fly) { // Property 'fly' does not exist on type 'Fish'
//     pet.fly();
// }

// 使用类型断言解决报错
if ((pet).swim) {
  (pet).swim();
} else if ((pet).fly) {
  (pet).fly();
}

用户自定义类型保护

  • 定义一个函数,它的返回值是一个类型谓词
  • 谓词为parameterName is Type这种形式,parameterName必须是来自于当前函数签名里的一个参数名
// 用户自定义类型保护:定义一个函数,它的返回值是一个类型谓词:
// flag is Fish就是类型谓词。 谓词为parameterName is Type这种形式,parameterName必须是来自于当前函数签名里的一个参数名
function isBird(flag: Fish | Bird): flag is Bird {
  return (flag).fly !== undefined;
}

if (isBird(pet)) {
  pet.fly();
} else {
  pet.swim();
}

typeof 类型保护

  • string number boolean symbol

instanceof 类型保护

  • instanceof类型保护是通过构造函数来细化类型
// typeof 类型保护  string number boolean symbol
function padLeft(value: string, padding: string | number) {
  if (typeof padding === "number") {
    return Array(padding + 1).join(" ") + value;
  }
  if (typeof padding === "string") {
    return padding + value;
  }
  throw new Error(`Expected string or number, got '${padding}'.`);
}

// instanceof 类型保护
// instanceof类型保护是通过构造函数来细化类型

类型保护去除编译器校验 null

  • 代码判断
  • 短路运算符
  • !符号
// 类型保护去除 null
// 判断
function remove(p: string | null) {
  if (p === null) return "default";
  return p;
}
console.log(remove("1")); // '1'
console.log(remove(null)); // 'default'

// 短路运算符
function remove2(p: string | null) {
  return p || "default";
}
console.log(remove2("1")); // '1'
console.log(remove2(null)); // 'default'

// ! 用在赋值的内容后时,使null和undefined类型可以赋值给其他类型并通过编译,表示该变量值可空
function remove3(p: number | null): string {
  const n = p!.toFixed();
  return n;
}

console.log(remove3(null));
export {};

类型缩减

type URStr = "string" | string; // 类型是 string
type URNum = 2 | number; // 类型是 number
type URBoolen = true | boolean; // 类型是 boolean
enum EnumUR {
  ONE,
  TWO,
}
type URE = EnumUR.ONE | EnumUR; // 类型是 EnumUR

// 当联合类型的成员是接口类型 如果满足其中一个接口的属性是另外一个接口属性的子集,这个属性也会类型缩减
type UnionInterce = { age: "1" } | { age: "1" | "2"; [key: string]: string };
const eg: UnionInterce = {age:'1', name:'2'};

13.Symbol

  • Symbols是不可改变且唯一的
  • symbol类型的值是通过Symbol构造函数创建的
  • Symbol 方法创建值的时候不用使用 new 操作符,原因是通过 new 实例化的结果是一个 object 对象,而不是原始类型的 symbol
  • Symbol 方法接收一个参数,表示对生成的 symbol 值的一种描述
const a: symbol = Symbol("k");
const b: symbol = Symbol("k");

console.log(a === b); // false

Symbol.for( ) Symbol.keyFor( )

// Symbol.for 方法可以检测上下文中是否已经存在使用该方法且相同参数创建的 symbol 值; 如果存在则返回已经存在的值,如果不存在则新建。
// Symbol.keyFor 方法返回一个使用 Symbol.for 方法创建的 symbol 值的 key
const c:symbol = Symbol('g');

console.log(Symbol.for('k'));  // Symbol(k)

const ok : symbol = Symbol.for('ok')
console.log(ok);  //Symbol(ok)
console.log(Symbol.keyFor(ok));  //ok

symbol的使用场景

// 1 作为对象属性 当一个复杂对象中含有多个属性的时候,很容易将某个属性名覆盖掉,利用 Symbol 值作为属性名可以很好的避免这一现象
const obj = {
    [Symbol('name')] : 'o'
}

// 2 模拟私有属性 (ts private)
const size = Symbol('size');
class Collection {
  constructor() {
    this[size] = 0;
  }

  add(item) {
    this[this[size]] = item;
    this[size]++;
  }

  static sizeOf(instance) {
    return instance[size];
  }
}

const x = new Collection();

14.Iterator 迭代器

  • 当一个对象实现了Symbol.iterator属性时,我们认为它是可迭代的。
  • 一些内置的类型如Array,Map,Set,String,Int32Array,Uint32Array等都已经实现了各自的Symbol.iterator。
  • 对象上的Symbol.iterator函数负责返回供迭代的值
// Iterators 迭代器
// for..of会遍历可迭代的对象,调用对象上的Symbol.iterator方法

let someArray = [1, "string", false];

for (let entry of someArray) {
  console.log(entry); // 1, "string", false
}

for..of和for..in

// 区别1
// 用于迭代的值却不同,for..in迭代的是对象的 键 的列表,而for..of则迭代对象的键对应的值
let list = [4, 5, 6];

for (let i in list) {
  console.log(i); // "0", "1", "2",
}

for (let i of list) {
  console.log(i); // "4", "5", "6"
}

// 区别2
// for..in可以操作任何对象;它提供了查看对象属性的一种方法。 但是for..of关注于迭代对象的值。内置对象Map和Set已经实现了Symbol.iterator方法
let pets: any = new Set(["Cat", "Dog", "Hamster"]);
pets["species"] = "mammals";

for (let pet in pets) {
  console.log(pet); // "species"
}

for (let pet of pets) {
  console.log(pet); // "Cat", "Dog", "Hamster"
}

15. interface vs type

  • TypeScript 的核心原则之一是对值所具有的结构进行类型检查。 而接口的作用就是为这些类型命名和为你的代码或第三方代码定义数据模型。
  • type 会给一个类型起个新名字。 type 可以作用于原始值(基本类型),联合类型,元组以及其它任何你需要手写的类型
// interface 和 type
type nameType = string;
type nameFnType = () => string;
type unionType = nameType | nameFnType;

// 类型别名也可以是泛型
type ageType = {
  age: T;
};
// 使用类型别名来在属性里引用自己
type sizeType = {
  value: sizeType;
};

1. 描述对象和函数

interface Iobj {
  name: string;
}

type Tobj = {
  name: string;
};

interface IFn {
  (name: string): number;
}
const ff: IFn = (name: string) => {
  return 1;
};

type TFn = (name: string) => number;
const fff: TFn = (name: string) => {
  return 1;
};

2. 与接口不同,类型别名还可以用于其他类型,如基本类型(原始值)、联合类型、元组。

3. extend

  • 两者都可以扩展,但是语法不同。此外,请注意接口和类型别名不是互斥的。接口可以扩展类型别名,反之亦然。
// 接口继承
interface IPX {
  x: number;
}
interface IPT extends IPX {
  y: number;
}
// type继承
type TPX = { x: number };
type TPT = TPX & { y: number };
// 接口继承type
interface IextendT extends TPX {
  z: number;
}
const ipx: IextendT = { x: 1, z: 2 };

// type 继承接口
type TextendI = IPX & { z: number };
const tpx: TextendI = { z: 1, x: 2 };

4. class Implements

// 类可以以相同的方式实现接口或类型别名。但是,类和接口被认为是静态的。因此,它们不能实现/扩展命名[联合类型]的类型别名
interface Point {
  x: number;
  y: number;
}

class SomePoint implements Point {
  x: number = 1;
  y: number = 2;
}

type Point2 = {
  x: number;
  y: number;
};

class SomePoint2 implements Point2 {
  x: number = 1;
  y: number = 2;
}

type PartialPoint = { x: number } | { y: number };
// error:A class can only implement an object type or intersection of object types with statically known members
class SomePartialPoint implements PartialPoint {
  x: number = 1;
  y: number = 2;
}

5 . Declaration merging & exports

// 与类型别名不同,接口可以定义多次,并将被视为单个接口(合并所有声明的成员)。
interface Point {
  x: number;
}
interface Point {
  y: number;
}

const point: Point = { x: 1, y: 2 };

type Keys = "firstname" | "surname";

type DudeType = {
  [key in Keys]: string;
};

const test: DudeType = {
  firstname: "Pawel",
  surname: "Grzybek",
};

// error : A mapped type may not declare properties or methods.ts(7061)
interface DudeType2 {
  [key in keys]: string;
}

export default interface Config {
  name: string;
}

// export default type Config1 = {
//   name: string
// }
// 会报错

type Config2 = {
  name: string;
};
export { Config2 };

小结

  • interface 和 type 很像,很多场景,两者都能使用。
  • 但也有细微的差别:类型:对象、函数两者都适用,但是 type 可以用于基础类型、联合类型、元祖。
  • 同名合并:interface 支持,type 不支持。
  • 计算属性:type 支持, interface 不支持。
  • 总的来说,公共的用 interface 实现,不能用 interface 实现的再用 type 实现。是一对互帮互助的好兄弟

16.keyof 索引查询

  • 对应任何类型T, keyof T 的结果为该类型上所有公有属性key的联合
  • T[K] 索引访问
  • T[keyof T] 的方式,可以获取到T所有key的类型组成的联合类型
  • T[keyof K]的方式,获取到的是T中的key且同时存在于K时的类型组成的联合类型
// keyof 索引查询
// 对应任何类型T,keyof T的结果为该类型上所有公有属性key的联合
interface A {
    name:string;
    age:number;
};
type B  = keyof A;
let b : B = 'name';

class C {
    public name : string = '';
    private age : number = 0;
    protected job : string = ''
}
const c : keyof C = 'name';  // keyof 只能拿到公有属性

// T[K] 索引访问
// T[keyof T] 的方式,可以获取到T所有key的类型组成的联合类型
type D = A[keyof A];  // D string | number
const d: D = 0;
type E = A['name'];

// T[keyof K]的方式,获取到的是T中的key且同时存在于K时的类型组成的联合类型
type FF = { name: string };
type F = A[keyof FF];
const f: F = "string type";

export {}

17.ts类型体操

TypeScript 高级类型会根据类型参数求出新的类型,这个过程会涉及一系列的类型计算逻辑,这些类型计算逻辑就叫做类型体操。当然,这并不是一个正式的概念,只是社区的戏称,因为有的类型计算逻辑是比较复杂的。

// 类型计算之后生成的类型
type Pick = {
  [P in K]: T[P];
};

// in 执行的是 for ...in 的操作
type Eg = {
    [P in 'x'| 'y'] : P
}
type Egs = {
    x: "x";
    y: "y";
}

type Father = {
    name:string;
    age:number;
    job:string;
}

type K = 'name' | 'age';

interface Child extends Pick {}

const c : Child = {
    job:'xxx', // 'job' does not exist in type 'Pick'
    age:1,
    name:'name'
}

// 类型复制
type Mather = { [K in keyof Father]: Father[K] };
const m: Mather = {
  name: "name",
  job: "job",
  age: 1,
};

export {};

你可能感兴趣的:(typescript学习笔记)