TypeScript学习笔记-5-tsc指令、TS配置、部分更新功能、通用类型

5,779 阅读6分钟

tsc指令

首先下载TypeScript

我选择了npm安装的方式:

npm install typescript --save-dev

通过npx tsc运行TypeScript编译器。

执行tsc指令会编译tsconfig.json定义的最近的一个项目。也可以传递你想要编译的文件名tsc index.ts。需要全局安装Typescript才能直接使用tsc指令,只在某个项目中安装了使用npx tsc

前面的文章中已经提到过--strictNullChecks,更多的选项可以看tsc指令选项

TS配置

目前的tsconfig.json文件是这样的:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ES2020",
    "baseUrl": "./",
    "jsx": "react",
    "paths": {
      "@/*": ["src/*"]
    },
    "moduleResolution": "node",
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "strictNullChecks": true,
    "noImplicitThis": true,
  },
  "include": [
    "src/*",
    "src/**/*",
    "typings/*",
    "__tests__/*",
    "__tests__/**/*"
  ]
}

这个配置是在练习的过程中因为遇到一些问题一点点加上来的。

target设置为ES2020,为了识别ES2020语法。

"module": "ES2020", , 处理动态导入模块的时候报错:Dynamic imports are only supported when the '--module' flag is set to 'es2020', 'esnext', 'commonjs', 'amd', 'system', or 'umd'.

"baseUrl": "./", ,必须设置了baseUrl选项,才能使用paths选项。

"jsx": "react",jsx用来设置jsx语法是以什么方式转换为JavaScript的。

"paths": { "@/*": ["src/*"] },,因为在Webpack中定义了路径的别名@,在tsconfig.json中也需要做相应的配置。

"moduleResolution": "node",,处理import报错:Cannot find module 'axios' or its corresponding **type** declarations.

"experimentalDecorators": true,,为了识别装饰器语法。

"strictNullChecks": true,,开启严格的null检查。

完整的tsconfig配置

部分更新功能

1.可选链

let x = foo?.bar.baz();

等同于:

let x = foo === null || foo === undefined ? undefined : foo.bar.baz();

2.空位合并

let x = foo ?? bar();

等同于:

let x = foo !== null && foo !== undefined ? foo : bar();

3.断言函数

assert(someValue === 42);

如果someValue === 42为假,就会抛出一个AssertionError

通用类型

TypeScript提供了一系列通用类型促进通用类型转换。这些通用类型是全局可访问的。

Partial<Type>

构造一个类型包含Type的所有属性并且将属性设置为可选的。这个通用方法会返回一个代表给出的类型的所有子集的类型。

interface A {
  m1: number;
  m2: number;
}

const a: Partial<A> = {

};

a赋值一个空对象没有报类型错误是因为类型Partial<A>将类型Am1m2属性都变成可选的了。

Readonly<Type>

构造一个包含Type的所有属性的类型,属性被设置为只读,意味着构造类型的属性不能被重新赋值。

interface A {
  m1: number;
  m2: number;
}

const a1: Readonly<A> = {
  m1: 1,
  m2: 2,
}
a1.m1 = 123; // 报错:Cannot assign to 'm1' because it is a read-only property.

m1属性赋值会报错,因为类型Readonly<A>将类型A的属性都变为只读的了。

Record<Keys,Type>

构造了一个包含类型Type的属性Keys的集合。这个通用函数能被使用来匹配一个类型的属性到另一个类型。

interface A1 {
  a1: number;
}

type Keys = 'm1' | 'm2';

const a2: Record<Keys, A1> = {
  m1: { a1: 1 },
  m2: { a1: 1 },
}

Keys是对象的属性名的类型,A1是对象的属性值的类型。

Pick<Type, Keys>

通过从类型中挑选出属性Keys的集合构造一个类型。将类型Type中的部分属性拿出来作为一个新的类型。

interface A2 {
  m1: number;
  m2: string;
  m3: string;
}

const a3: Pick<A2, 'm1' | 'm2'> = {
  m1: 1,
  m2: '2',
}

Pick<A2, 'm1' | 'm2'>类型从类型A2中拿出了m1m2属性组成一个新的类型。

Omit<Type, Keys>

构造一个从Type中挑选出了所有属性的类型,然后移除Keys。将类型Type中的部分属性剔除掉,剩下的属性作为一个新的类型。

interface A2 {
  m1: number;
  m2: string;
  m3: string;
}

const a4: Omit<A2, 'm1' | 'm2'> = {
  m3: 'string',
}

Omit<A2, 'm1' | 'm2'>类型从类型A2中剔除了m1m2属性,剩下的属性组成一个新的类型。

Exclude<Type, ExcludedUnion>

通过从Type中排除所有可以赋值给ExcludedUnion的联合成员来构造一个类型。

type A3 = Exclude<'m1' | 'm2' | 'm3', 'm1'>;

let a5: A3 = 'm2';
a5 = 'm3';

现在 Exclude<'m1' | 'm2' | 'm3', 'm1'>类型相当于类型'm2'|'m3'。在'm1' | 'm2' | 'm3'类型中,能赋值给'm1'的类型是'm1',把这个类型排除,剩下的就是类型'm2'|'m3'

Extract<Type, Union>

通过从Type中取出所有可以赋值给Union的联合成员来构造一个类型。

type A4 = Extract<'m1' | 'm2' | 'm3', 'm1'>;

const a6: A4 = 'm1';

类型'm1' | 'm2' | 'm3'中,能赋给字面量类型'm1'的只有m1,所以A4的类型就是'm1'

NonNullable<Type>

构造一个从Type中移除了nullundefined的类型。

type T5 = NonNullable<undefined | null | number>;

let a7: T5 = 1;
a7 = undefined; // 报错:Type 'undefined' is not assignable to type 'number'.

Parameters<Type>

从一个函数类型Type的参数使用的类型构造一个元组类型。

type T6 = Parameters<(x: number, y: number) => number>;

const a8: T6 = [1, 2];

T6的类型相当于[x: number, y: number]

ConstructorParameters<Type>

从构造函数的类型中构造一个元组或者数组类型。这会是一个元组类型包含所有的参数类型(或者never类型如果Type不是一个函数的话)。

interface A5 {
  new (x: number, y: string): number;
}

type T7 = ConstructorParameters<A5>;

const a9: T7 = [1, '2'];

T7的类型是[x: number, y: string]

ReturnType<Type>

构造一个由类型为Type的函数类型的返回类型组成的类型。

type T8 = ReturnType<() => number>;

const a10: T8 = 123;

ReturnType<() => number>的类型就是number

InstanceType<Type>

构造出一个类型为Type的构造函数的实例的类型。

class ClassA {
  m1: number;
  constructor (x) {
    this.m1 = x;
  }
  m2 (y: number) {
    return y;
  }
}

type T9 = InstanceType<typeof ClassA>;

const a11: T9 = {
  m1: 1,
  m2: (y) => {
    return y;
  },
}

Required<Type>

造一个类型组合,所有Type的属性都被设置为必须。和Partial相反。

interface A5 {
  m1?: number;
  m2?: string;
}

type T10 = Required<A5>;

const a12: T10 = {
  m1: 1,
  m2: '2'
}

ThisParameterType<Type>

取出一个函数类型的this参数的类型,或者是unknown如果函数类型没有this参数的话。

interface A6 {
  (this: number, x: string, y: number): void;
}

type T11 = ThisParameterType<A6>;

const a13: T11 = 1;

T11的类型就是this参数的类型,在这个例子中是number

OmitThisParameter<Type>

Type中移除掉this参数。如果Type没有明确地声明this参数,结果就是简单的Type,否则,一个新的没有this参数的函数类型会从Type被创建。泛型被去掉了,并且只有最后一个重载标志能传递到新的函数类型中。

interface A6 {
  (this: number, x: string, y: number): void;
}

type T12 = OmitThisParameter<A6>;

const a14: T12 = function (x: string, y: number) {
  console.log('a');
}

T12的类型是类型A6移除掉this参数的类型,即(x: string, y: number) => void

ThisType<Type>

这个通用方法不返回一个转化后的类型。相反地它作为一个上下文this类型的标记来处理。注意 --noImplicitThis标志必须能够在这个通用方法中被使用。 比如官网的这个例子中,使用ThisType<D & M>标明在methods中,this的类型是D&M

type ObjectDescriptor<D, M> = {
  data?: D;
  methods?: M & ThisType<D & M>; // Type of 'this' in methods is D & M
};

function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
  let data: object = desc.data || {};
  let methods: object = desc.methods || {};
  return { ...data, ...methods } as D & M;
}

let obj = makeObject({
  data: { x: 0, y: 0 },
  methods: {
    moveBy(dx: number, dy: number) {
      this.x += dx; // Strongly typed this
      this.y += dy; // Strongly typed this
    },
  },
});

obj.x = 10;
obj.y = 20;
obj.moveBy(5, 5);
TypeScript学习笔记
《TypeScript学习笔记-1-基础类型、字面量类型、类型声明》
《TypeScript学习笔记-2-联合类型&交叉类型、泛型、类型守卫、类型推断》
《TypeScript学习笔记-3-枚举、函数、类、装饰器》
《TypeScript学习笔记-4-模块、命名空间》
当前篇《TypeScript学习笔记-5-tsc指令、TS配置、部分更新功能、通用类型》