引子
越来越发觉基础的重要性,在工作中经常有基础不牢,地动山摇的感觉。所以在学习新技术之前,我决定先夯实自己的基础。比如TypeScript知识,现在我决定系统学习一下TypeScript知识, 主要的学习资源是basarat.gitbook.io, 中文翻译指路👉 编译上下文 | 深入理解 TypeScript~
Why TypeScript
(这一章主要介绍为什么需要TS,涉及到比较多的JS概念,总结就是下面一句话,其余内容先跳过~)
TS的诞生主要是两个目的:①给JS提供类型系统,②为现在的JS引擎引入未来的JS版本语法等
Project
这章将会介绍我们如何在自己的项目中成功引入TS
Compilation Context
在TS中,编辑上下文指的是 一组要被编译的文件以及相关的编辑选项, 最常见的方式是用 tsconfig.json
来定义这个编译上下文
接下来我们学习一下tsconfig.json文件的内容:
最简单的就是在根目录下面的空的JSON文件,这样会指定所有的.ts文件为编译的文件,同时会包含几个默认的编译选项
ts
{}
compilerOptions
你可以自定义编译选项使用 compilerOptions, 如下:
ts
{
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'commonjs', 'amd', 'system', 'umd' or 'es2015'. */
"lib": [], /* Specify library files to be included in the compilation: */
"allowJs": true, /* Allow JavaScript files to be compiled. */
"checkJs": true, /* Report errors in .js files. */
"jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
"declaration": true, /* Generates corresponding '.d.ts' file. */
"sourceMap": true, /* Generates corresponding '.map' file. */
"outFile": "./", /* Concatenate and emit output to single file. */
"outDir": "./", /* Redirect output structure to the directory. */
"rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
"removeComments": true, /* Do not emit comments to output. */
"noEmit": true, /* Do not emit outputs. */
"importHelpers": true, /* Import emit helpers from 'tslib'. */
"downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
"isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
"strictNullChecks": true, /* Enable strict null checks. */
"noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
"alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
/* Additional Checks */
"noUnusedLocals": true, /* Report errors on unused locals. */
"noUnusedParameters": true, /* Report errors on unused parameters. */
"noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
"noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
/* Module Resolution Options */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
"baseUrl": "./", /* Base directory to resolve non-absolute module names. */
"paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
"rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
"typeRoots": [], /* List of folders to include type definitions from. */
"types": [], /* Type declaration files to be included in compilation. */
"allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
/* Source Map Options */
"sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
"mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */
"inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
"inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
/* Experimental Options */
"experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
"emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */
}
}
Which Files?
使用include和exclude来说明包含哪些文件或文件夹:
ts
{
"include":[
"./folder"
],
"exclude":[
"./folder/**/*.spec.ts",
"./folder/someSubFolder"
]
/*OR*/
"files":[
"./some/file.ts"
]
}
默认只匹配特定的文件类型:
.ts, .tsx 是默认匹配的类型
如果在 tsconfig.json 中设置了 allowJs: true,那么 .js, .jsx 也会被包含
Declaration Spaces
在TS中有两个命名空间,一个是变量的命名空间,另外一个是类型的命名空间
Type Declaration Space
类型声明空间意味着你可以使用它们作为类型注释:
ts
/*类型声明*/
class Foo {}
interface Bar {}
type Bas = {}
/*使用*/
var foo: Foo;
var bar: Bar;
var bas: Bas;
Note: 尽管有interface Bar , 无法被直接当作变量使用的
ini
interface Bar {};
var bar = Bar; // ERROR: "cannot find name 'Bar'"
Variable Declaration Space
变量命名空间意味着你可以直接当作变量使用,有些类型命名空间也可以当作变量命名空间来使用
ts
class Foo {};
var someVar = Foo;
var someOtherVar = 123;
同样,我们使用 var 定义的变量只在变量命名空间有效,无法当作类型注释使用:
ts
var foo = 123;
var bar: foo; // ERROR: "cannot find name 'foo'"
/*不能找到name,是因为name foo不是被定义在类型命名空间*/
Modules
Global Module
默认情况你编写的TS文件中的代码都是在全局命名空间之下的~
ts
var foo = 123;
/*其他文件*/
var bar = foo; // allowed
File Module
也称为外部模块。如果你在 TypeScript 文件的顶层使用了 import
或 export
,那么它会在该文件中创建一个本地作用域。
ini
export var foo = 123;
这时在全局就没有foo,如果你希望在另外一个文件中引用foo, 那么必须显式引入它:
ts
import { foo } from "./foo";
var bar = foo; // allowed
同时使用import也表明这个文件也是文件模块,不会污染全局命名空间
使用File Module的TypeScript 文件会生成什么样的 JavaScript 代码,是由一个名为
module
的编译器选项决定的。
File Module Details
通过指定 module
选项,可以将TS生成不同的JS代码,但是由于AMD和SystemJS, ESM的各种兼容性问题,我们推荐使用 module: commonjs
, 但是在编写代码的时候使用ESM
Summary: Use
module:commonjs
and use the ES module syntax to import / export / author modules.
Module paths
当你设置了
module: commonjs
时,默认使用moduleResolution: "Node"
模块主要有两种不同类型。这种区分是由 import
语句中的路径部分决定的:
- 相对路径模块(Relative path modules)(路径以
.
开头,例如./someFile
或../../someFolder/someFile
等) - 其他动态查找模块(Dynamic lookup)(例如
'core-js'
、'typestyle'
、'react'
,甚至'react/core'
等 => 采用Node.js 默认的"逐级向上查找"策略
Node.js 默认的"逐级向上查找"策略:
./node_modules/foo
../node_modules/foo
../../node_modules/foo
...
直到文件系统的根目录
这两类模块的主要区别在于:它们在文件系统上的解析方式不同。
现在我们知道这两种模块查找哪些地方了,那么我们要查找什么文件呢?
会按照以下的顺序查找:
- 如果是文件,直接是:foo.ts
- 如果是文件夹,查找foo/index.ts
- 如果是文件夹,且有foo/package.json,且存在
types
指定的文件,就是我们查找的目标文件 - 同上,不过是
main
字段指定的文件
这个文件指的是 .ts / .d.td / .js 文件
Overturning dynamic lookup just for types
对于动态查找模块,我们可以使用一些方式覆盖默认的查找规则
ts
// global.d.ts
declare module 'foo' {
// Some variable declarations
export var bar: number; /*sample*/
}
这样就有一个模块叫做foo, 其中声明了bar变量
ts
// anyOtherTsFileInYourProject.ts
import * as foo from 'foo';
// TypeScript assumes (without doing any lookup) that
// foo is {bar:number}
import/require for importing type only
ts
import foo = require('foo');
在上面这个表达式中,你引入了foo变量,如果你用类型的方式使用它,那么foo文件就不会包含在编辑之后的代码中,如果你以变量的方式使用它,那么就会包含在编译之后的JS文件中
假设我们的foo文件是这样的:
ts
// foo.ts
export let someValue: number;
export function greet(name: string): string {
return `Hello, ${name}!`;
}
export interface A {
name: string,
id: number
}
```ts
下面举几个例子说明问题:
/Example 1/ import foo = require('foo'); generate the JavaScript :
/Example 2/ import foo = require('foo'); var bar: foo; generate the JavaScript : var bar;
/Example 3/ import foo = require('foo'); var bar = foo; generate the JavaScript : var foo = require('foo'); var bar = foo;
php
**使用例子:懒加载**
```ts
import foo = require('foo');
export function loadFoo() {
// 这是懒加载 foo,原始的加载仅仅用来做类型注解
const _foo: typeof foo = require('foo');
// 现在,你可以使用 `_foo` 替代 `foo` 来作为一个变量使用
}
使用例子:确保导入
如果仅仅是使用模块的副作用,但是害怕JS编译代码的时候排除,这个时候可以 ensureImport 变量,确保编译的JavaScript依赖与模块:
ts
import foo = require('./foo');
import bar = require('./bar');
import bas = require('./bas');
const ensureImport: any = foo || bar || bas;
Dynamic Import Expressions
tsconfig.json
中的 "module"
设置:
-
如果使用
"module": "ESNext"
或"ES2020"
: TypeScript 会保留import()
语法,交给打包器(如 Webpack)处理,因此可以实现 懒加载 / 代码分割。 -
如果使用
"module": "CommonJS"
:TypeScript 会尝试编译 import() 为 require() 或根本无法识别这个用法,结果会导致:- 打包器无法做代码分割;
- 有些配置下甚至会直接报错。
TypeScript's Type System
JS Migration Guide
常见的这里就不提了,主要说一些大家可能不知道或不会注意到的~
我们可能会看到内联类型声明,但是我们可能没怎么接触到这个概念,类似下面这种:
ts
var name1: {
first: string;
second: string;
};
name1 = {
first: 'John',
second: 'Doe'
};
name1 = { // Error : `second` is missing
first: 'John'
};
name1 = { // Error : `second` is the wrong type
first: 'John',
second: 1337
};
什么时候使用 inline 类型?
- 简单结构、只用一两次:适合用 inline type。
- 结构复杂、重复使用:建议用 interface 或 type alias 来提取,更可读也更易维护。
Third Party ****JavaScript
你不能强迫所有第三方库都写 TypeScript,但你可以通过 .d.ts
文件告诉 TypeScript 编译器这些库的接口或变量"确实存在"。
ts
// vendor.d.ts
declare var someGlobalLib: any;
// some-lib.d.ts
declare module 'some-lib' {
export function doSomething(): void;
}
对于流行库,强烈建议使用 DefinitelyTyped 提供的完整类型定义:
css
npm install --save-dev @types/jquery
安装完后,你就可以像这样导入,同时拥有完善的类型支持:
ts
import * as $ from "jquery";
$('div').fadeOut(); // 会有类型提示和错误检查
同时,由于TypeScript默认只认识 .ts/.js 文件,所以类型于下面这样的下发会报错:
javascript
import styles from './styles.css';
/*Cannot find module './styles.css' or its corresponding type declarations.*/
解决方法就是在 .d.ts 文件加上:
ts
// global.d.ts
declare module '*.css';
declare module '*.html';
这样就告诉 TypeScript:"你放心,这类文件存在,并且我知道怎么处理它们(即使我现在不关心具体类型)"
@types
@types
是一个第三方提供的 TypeScript 类型声明文件库 ,由社区维护并托管在 DefinitelyTyped 仓库中。
例如,jquery
是用 JavaScript 写的,没有内建类型定义文件。为了让 TypeScript 能正确识别其类型信息,我们可以安装:
ts
npm install @types/jquery --save-dev
@types支持全局和模块类型定义
然而,一些@types类型声明文件会默认向全局作用域注入类型和变量(比如@types/node会在全局注入process, global, require等等变量), 这可能会引发冲突或"污染"项目中的全局命名空间
为了控制哪些类型可以注入到全局 作用域 ,TypeScript 提供了 tsconfig.json
中的 compilerOptions.types
配置项。
ts
{
"compilerOptions": {
"types": ["jquery"]
}
}
/*
如果不配置 types,那么 TypeScript 会默认加载所有安装的 @types 包里的全局类型。
配置了之后,那么 只有 @types/jquery 中的类型声明会生效,其他比如 @types/node 的全局声明就不会影响到你当前的项目,除非你也把 "node" 加进去。
*/
Ambient Declarations
如果写得是一个以.d.ts
为扩展名的类型声明文件(.d.ts
文件专门用来写类型信息,不会被编译成 JavaScript。),那么其中每个顶层声明都要以declare
开头,都是为了告诉TypeScript,这是一个类型声明,不是实际的可执行代码
Interfaces
在TS中,接口是一个很重要的概念,它可以将一组类型组合在一起,按照你预期的方式组织变量结构。我们可以使用内联注释和接口注释来创建一个新的类型,但是接口注释允许我们对于这个类型不断扩展:
ts
// Lib a.d.tsinterface
Point {
x: number,
y: number}declare const myPoint: Point
// Lib b.d.tsinterface
Point {
z: number
}// Your code
myPoint.z // Allowed!
(接口是开放的,这是TS的一个重要原则,允许接口来模仿JS的可扩展性)
同时,接口的开放性还体现在类可以去实现接口,像下面这种:
ts
class myPoint implements Point {
x: number;
y: number;
z: number
}
Enum
基本用法:
ts
/*类型推断*/
enum CardSuit {
Clubs = '0',
Diamonds = '1',
Hearts = '2',
Spades = '3'
}
let Card = CardSuit.Clubs // 自动推断Crad是CardSuit类型
Card = CardSuit.Diamonds
/*多个Enum结构会自动合并*/
enum Color {
DarkRed = 5,
DarkGreen,
DarkBlue
}
enum Color {
Red,
Green,
Blue
}
let a : Color = Color.Blue
常量枚举:
我们经常看到这两种写法,后者被叫做常量枚举
ts
/*第一种*/
enum Tristate {
False,
True,
Unknown
}
const lie = Tristate.False;
会被编译成👉 let lie = Tristate.False
/*第二种*/
const enum Tristate {
False,
True,
Unknown
}const lie = Tristate.False;
会被编译成👉 let lie = 0
const enum
只能在 TypeScript 编译时使用,它在运行时是不存在的。
const enum
更高效,但只能用于静态、明确的代码场景 ,不能用在需要运行时访问枚举对象的地方。
lib.d.ts
在我们安装TypeScript的时候,会默认安装一个 lib.d.ts
声明文件,这个文件会包含JS运行时以及DOM中各种常见的环境声明
Eg:
ts
const foo = 123;
const bar = foo.toString();
当TS推断出foo是一个string,为什么可以知道它身上的toString方法呢?就是因为这个文件上面定义了,我们可以在 tsconfig.json
里面配置这个选项:
ts
{
"compilerOptions": {
"target": "ESNext",
"module": "ESNext",
"moduleResolution": "Node",
"esModuleInterop": true,
"strict": true,
"outDir": "./dist",
"strictNullChecks": false,
// "noLib": true
},
"include": ["src", "global.d.ts"]
}
当设置了 "noLib": true
,可以观察到foo.toString()
会立即报错
同样,我们编写.d.ts
或者 global.d.ts
文件扩展 lib.d.ts
声明文件:
ts
interface Window {
helloWorld(): void
}
这样在window对象身上就有了helloWorld
方法
--lib
选项
有的时候,我们希望解耦编译目标和环境库目标之前的关系。比如对于Promise编译目标是 --target es5, 但是我们需要在环境库中使用它,这时我们可以在tsconfig.json
中配置:
ts
"compilerOptions":
{
"target": "es5",
"lib": ["dom", "es6"]
}
Functions
函数声明有两种写法,之前不知道:
ts
/*函数声明*/
type barr1 = {
(a: number) : number
}
type barr2 = (a: number) => number
const barr1C:barr1 = (a) => {
return a
}
const barr2C:barr2 = (a) => {
return a
}
但是注意的是函数重载只能使用function函数声明,如果想要这么写,那么只能使用下面的方式:
ts
interface Overloaded {
(foo:string): string
(foo:number):number
}
function stringOrNumber(foo:number):number
function stringOrNumber(foo:string):string
function stringOrNumber(foo: any) : any {
if (typeof foo === 'number') {
return foo * foo;
} else if (typeof foo === 'string') {
return `hello ${foo}`;
}
}
const overloaded: Overloaded = stringOrNumber;
const str = overloaded('');
const num = overloaded(123);
readonly
全部属性变为只读:
ts
type Foo3 = {
bar : number;
baz : number
}
type Foo3readonly = Readonly<Foo3>
const foo3: Foo3 = {bar: 123, baz: 456}
const foo3readonly : Foo3readonly = {bar: 1233, baz: 4566}
foo3.bar = 456
foo3readonly.bar = 22312
部分属性变为只读:
ts
type Foo3 = {
bar : number;
baz : number
}
type Foo3readonly<T, K extends keyof T> = Readonly<Omit<T, K>> & Pick<T, K>
const foo3: Foo3 = {bar: 123, baz: 456}
const foo3readonly : Foo3readonly<Foo3, 'bar'> = {bar: 1233, baz: 4566}
foo3.bar = 456
foo3readonly.bar = 22312
绝对不可变:
ts
interface Foo4 {
readonly [x: number] : number
}
const foo4: Foo4 = {0:123,1:1212}
console.log(foo4[0]);
如果想以不变的方式使用原生 JavaScript 数组,可以使用 TypeScript 提供的 ReadonlyArray<T>
接口:
ts
let foo: ReadonlyArray<number> = [1, 2, 3];console.log(foo[0]); // ok
foo.push(4); // Error: ReadonlyArray 上不存在 `push`,因为他会改变数组
foo = foo.concat(4); // ok, 创建了一个复制
注意:readonly
能确保"我"不能修改属性,但是当你把这个属性交给其他并没有这种保证的使用者(允许出于类型兼容性的原因),他们能改变它。
Type Compatibility
在 TypeScript 中,函数参数类型既允许"变宽",也允许"变窄" ,默认不报错,这种设计叫做"双向协变"。
ts
interface Point2D {
x: number;
y: number;
}
interface Point3D {
x: number;
y: number;
z: number;
}
let fn2D = (p: Point2D) => {};
let fn3D = (p: Point3D) => {};
// 两个方向都允许(默认):
fn3D = fn2D; // ✅ 合理:接受更宽的类型
fn2D = fn3D; // ✅ 默认也允许:接受更窄的类型(双向)
开启 tsconfig.json
的 strictFunctionTypes: true
后:
- 只允许"参数变宽" (即逆变)
- 拒绝"参数变窄"的赋值(更安全)
ts
let fn3D: (p: Point3D) => void;
let fn2D: (p: Point2D) => void;
fn3D = fn2D; // ✅ OK
fn2D = fn3D; // ❌ 报错(strictFunctionTypes 下)
Moving Types
TypeScript 类型系统非常强大,它支持其他任何单一语言无法实现的类型流动和类型片段。
比较常用的是使用 typeof
来捕获一个变量的类型:
ts
let foo = 123;let bar: typeof foo; // 'bar' 类型与 'foo' 类型相同(在这里是: 'number')
bar = 456; // ok
bar = '789'; // Error: 'string' 不能分配给 'number' 类型
keyof
操作符能让你捕获一个类型的键。例如,你可以使用它来捕获变量的键名称,在通过使用 typeof
来获取类型之后:
ts
/*注意:const定义的是字面量类型*/
const colors = {
red: 'red',
blue: 'blue'
};
type Colors = keyof typeof colors;
let color: Colors; // color 的类型是 'red' | 'blue'
color = 'red'; // ok
color = 'blue'; // ok
color = 'anythingElse'; // Error
Utility Types
在这章我主要会介绍常见的类型工具,主要参考资料来自 www.typescriptlang.org 和 type-fest, 可以到
github.com练习~
首先是TS内置的类型工具大多数看名词就可以知道使用规则:
ts
/*1.Awaited<Type>*/
type promiseType = Promise<string>
const p: promiseType = Promise.resolve('1212')
p.then((res: Awaited<promiseType>) => {
console.log('res:', res);
})
/*2.Partial<Type>*/
interface Todo {
title: string;
desp: string
}
function updateTodo(todo:Todo, fieldsToUpdate: Partial<Todo>) {
return {...todo, ...fieldsToUpdate}
}
const todo1 = {
title: 'organize desk',
desp: 'clear clutter'
}
const todo2 = updateTodo(todo1, {
desp: 'aaa'
})
/*3.Required<Type>*/
interface Props {
a?: number;
b?: string
}
const obj1: Props = {a: 5}
const obj2: Required<Props> = {a : 5, b: '12'}
/*4.Readonly<Type>*/
const todo3 = {
title: 'organize desk',
desp: 'clear clutter'
}
const readonlyTodo3 : Readonly<Partial<typeof todo3>> = {
title: '2'
}
console.log(readonlyTodo3.desp);
/*5.Record<Keys, Type>*/
type CatName = "miffy" | "boris" | "mordred"
interface CatInfo {
age: number;
breed: string
}
const cats: Record<CatName, CatInfo> = {
miffy: { age: 10, breed: "Persian" },
boris: { age: 5, breed: "Maine Coon" },
mordred: { age: 16, breed: "British Shorthair" },
}
cats.boris
/*6.Pick<Type, Keys>*/
interface todoPick {
title: string;
desc: string;
completed: boolean
}
type todoPreview = Pick<todoPick, 'completed'|'desc'>
const todopick: todoPreview = {
completed: false,
desc: 'sasasa'
}
/*7.Omit<Type, Keys>*/
interface TodoOmit {
title: string;
description: string;
completed: boolean;
createdAt: number;
}
type todoOmit = Partial<Omit<TodoOmit, 'title'>>
const todoOmit1: todoOmit = {
description: '1212'
}
/*8.Exclude<UnionType, ExcludedMembers>*/
type T0 = Exclude<'a'|'b'|'c', 'a'>
type T1 = Exclude<string | (() => void), Function>
type Shape = { kind: "circle"; radius: number } | { kind: "square"; x: number } | { kind: "triangle"; x: number; y: number };
type T3 = Exclude<Shape, {kind: 'square'}>
/*9.Extract<Type, Union>*/
type T11 = Extract<string | (() => void) | number, number>
/*10.NonNullable<Type>*/
type Tnotnull = NonNullable<string | null | undefined>
/*11.Parameters<Type>*/
declare function f1(args: {a: number, b: string}, args2 : number): void;
type Tparams = Parameters<() => string>
type Tparams1 = Parameters<(s: string) => void>
type Tparams2 = Parameters<<T>(args: T) => T>
type Tparams3 = Parameters<typeof f1>
// type Tparams3 = [args: {
// a: number;
// b: string;
// }, args2: number]
/*12.ReturnType<Type>*/
type Treturn0 = ReturnType<() => string>;
type Treturn1 = ReturnType<(s: string) => void>;
type Treturn2 = ReturnType<<T>() => T>;
type Treturn3 = ReturnType<<T extends U, U extends number[]>() => T>
/*13.InstanceType<Type>*/
class C {
x = 0;
y = 0
}
type instance0 = InstanceType<typeof C>
type instance1 = InstanceType<any>
接下来是type-fest提供的一些在项目中会用到的类型工具:
类型名 | 作用 | 示例 |
---|---|---|
Except<T, K> | 从 T 中排除某些键 | 类似 Omit<T, K>,但更安全 |
Merge<T, U> | 合并两个类型(U 优先) | {a: number} + {a: string, b: boolean} → {a: string, b: boolean} |
SetOptional<T, K> | 指定 K 中的键为可选 | SetOptional<{a: string, b: number}, 'a'> → {a?: string, b: number} |
SetRequired<T, K> | 指定 K 中的键为必选 | 与上相反 |
Simplify | 展开类型嵌套层级 | 展示更直观的结构,常用于调试复杂泛型 |
Jsonify | 转换为 JSON 兼容结构 | 移除函数、Symbol 等不可序列化内容 |
Opaque<Type, Token> | 创建不可直接访问的封装类型 | 用于品牌化 primitive(如 UserID) |
ValueOf | 取联合值类型 | {a: 1, b: 2} → `1 |
AsyncReturnType | 获取 async 函数的返回值类型 | () => Promise → number |
PackageJson | 已定义好结构的 npm package.json 类型 | 非常适合构建 CLI 工具时使用 |
Compiler Internals
关于TS的编译原理后面会出文章单独介绍
以上就是TS的基础知识,仅用于本人查漏补缺,看了一遍确实解答了我很多疑惑,后续会单独介绍TS的编译原理~