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>
将类型A
的m1
、m2
属性都变成可选的了。
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
中拿出了m1
和m2
属性组成一个新的类型。
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
中剔除了m1
和m2
属性,剩下的属性组成一个新的类型。
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
中移除了null
和undefined
的类型。
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配置、部分更新功能、通用类型》 |