从实践角度梳理一下常用的 TS 知识点和使用场景

想要学号 TS,官方文档多读,代码多写,无他。

类型和值

要想搞懂 TS,先理解这2个概念

类型就是变量的类型,类的类型(使用 interface 定义),函数的类型(参数和返回值)

声明类型的方法有6种

  • 使用类型别名
  • x: number
  • 使用枚举 enum
  • 使用接口 interface
  • 类声明 class c {}

什么是值?
声明一个变量,声明一个函数,然后这些变量、函数可以被调用,值是运行时的名字。

可索引类型

  1. interface StringArray {
  2. [index: number]: string;
  3. }
  4. let myArray: StringArray;
  5. myArray = ["Bob", "Fred"];
  6. let myStr: string = myArray[0];

interface 和 type 关键字

功能非常相近,但是又有点区别

interface 和 type 两个关键字的含义和功能都非常的接近。这里我们罗列下这两个主要的区别:

interface:

  • 同名的 interface 自动聚合,也可以跟同名的 class 自动聚合
    • 可以给函数挂属性
  • 只能表示 object、class、function 类型
  • 想要扩展接口,只能用继承的方式
  • 不能实现或者继承联合类型的 type

type:

  • 不仅仅能够表示 object、class、function
  • 不能重名(自然不存在同名聚合了),扩展已有的 type 需要创建新 type
  • 支持复杂的类型操作,比如说 & |
  • 想要扩展 type 定义的类型,使用 &

枚举

常用的有常量枚举,不生成类型,只使用枚举值。

  1. export const enum Direction {
  2. NORTH = "NORTH",
  3. SOUTH = "SOUTH",
  4. EAST = "EAST",
  5. WEST = "WEST",
  6. }

断言

有时候,我们比 TS 更清楚变量是什么类型,可以使用断言,通常我们在 React 中使用 as 语法。

  1. let someValue: any = "this is a string";
  2. let strLength: number = (someValue as string).length;

尖括号语法

  1. function getLength(something: string | number): number {
  2. if ((<string>something).length) {
  3. return (<string>something).length;
  4. } else {
  5. return something.toString().length;
  6. }
  7. }

类型保护

在特定的区块中保证变量属于某种确定的类型,可以在此区块中放心的引用此类型的属性,或者调用此类型的方法。

  • instanceof
  • typeof 判断类型
  • in 某个 key 存在于实例中
  • 保护函数

特殊的返回值 - 类型谓词

  1. function isNumber(x: any): x is number {
  2. return typeof x === "number";
  3. }
  4. function isString(x: any): x is string {
  5. return typeof x === "string";
  6. }

交叉类型 联合类型

  1. interface IPerson {
  2. id: string;
  3. age: number;
  4. }
  5. interface IWorker {
  6. companyId: string;
  7. }
  8. type IStaff = IPerson & IWorker;
  9. const sayHello = (name: string | undefined) => {
  10. /* ... */
  11. };

为函数指定类型

注意:使用变量定义、类型别名、接口约束函数,都是函数类型的声明定义,没有实现该函数

  1. // 返回值可以推断出来,不用写
  2. const add = (x: number, y: number) => x + y
  3. const add9 = (x: number, y: number): number => x + y

使用接口约束函数

  1. interface F {
  2. (a: number, b: number): number;
  3. }
  4. let add: F = (a, b) => a + b

类型别名约束函数

  1. type Add = (x: number, y: number) => number;
  2. let add: Add = (a, b) => a + b

变量定义函数

  1. let mySum: (x: number, y: number) => number
  2. mySum = function (x: number, y: number): number {
  3. return x + y;
  4. };

模块

第一,ES6 模块与 CommonJS 规范不要混用

有一种兼容性写法

导出 export =
导入 import from 或者 impot x = require

操作符

extends

  1. type num = {
  2. num:number;
  3. }
  4. interface IStrNum extends num {
  5. str:string;
  6. }
  7. // 与上面等价
  8. type TStrNum = A & {
  9. str:string;
  10. }

keyof

获取某种类型的所有 key 的联合类型

  1. interface Person {
  2. name: string;
  3. age: number;
  4. location: string;
  5. }
  6. type K1 = keyof Person; // "name" | "age" | "location"
  7. type K2 = keyof Person[]; // number | "length" | "push" | "concat" | ...
  8. type K3 = keyof { [x: string]: Person }; // string | number

泛型

泛型就是定义一种模板,例如ArrayList,然后在代码中为用到的类创建对应的ArrayList<类型>。

  • 实现了编写一次,万能匹配,又通过编译器保证了类型安全:这就是泛型
  • 泛型的好处是使用时不必对类型进行强制转换,它通过编译器对类型进行检查;
  • 泛型可以通过参数 决定引入的参数类型

参考 什么是泛型 《Java教程》廖雪峰

通常我们说,泛型就是指定一个表示类型的变量

「泛型就是不预先确定的数据类型,具体的类型在使用的时候再确定的一种类型约束规范」

「我们也可以把泛型变量理解为函数的参数,只不过是另一个维度的参数,是代表类型而不是代表值的参数。」**

泛型可以用于类的实例成员、类的方法、函数参数和函数返回值

泛型可以应用于 functioninterfacetype 或者 class 中。但是注意,「泛型不能应用于类的静态成员」

  1. interface GenericIdentityFn<T> {
  2. (arg: T): T;
  3. }
  4. class GenericNumber<T> {
  5. zeroValue: T;
  6. add: (x: T, y: T) => T;
  7. }
  8. let myGenericNumber = new GenericNumber<number>();
  9. myGenericNumber.zeroValue = 0;

泛型类型、泛型接口

  1. type Log = <T>(value: T) => T
  2. let myLog: Log = log
  3. interface Log<T> {
  4. (value: T): T
  5. }
  6. let myLog: Log<number> = log // 泛型约束了整个接口,实现的时候必须指定类型。如果不指定类型,就在定义的之后指定一个默认的类型
  7. myLog(1)

函数中省略尖括号,推断出要传入的类型

TS 会自动推断出要传的参数类型

  1. function identity <T, U>(value: T, message: U) : T {
  2. console.log(message);
  3. return value;
  4. }
  5. console.log(identity(68, "Semlinker"));

函数返回一个object,并使用泛型

  1. interface Iidentity<T, U> {
  2. value: T,
  3. message: U
  4. }
  5. function identity<T, U>(value: T, message: U): Iidentity<T, U> {
  6. return {
  7. value,
  8. message
  9. } ;
  10. }
  11. console.log(identity([12,3], 'hello'));

常见泛型变量代表的意思

  • T(Type):表示一个 TypeScript 类型
  • K(Key):表示对象中的键类型
  • V(Value):表示对象中的值类型
  • E(Element):表示元素类型

泛型类

泛型类可确保在整个类中一致地使用指定的数据类型。

我们来看一个泛型类实现泛型接口的例子

  1. interface GenericInterface<U> {
  2. value: U
  3. getIdentity: () => U
  4. }
  5. class IdentityClass<T> implements GenericInterface<T> {
  6. value: T
  7. constructor(value: T) {
  8. this.value = value
  9. }
  10. getIdentity(): T {
  11. return this.value
  12. }
  13. }
  14. // 可以省略 <number>
  15. const myNumberClass = new IdentityClass(68);
  16. console.log(myNumberClass.getIdentity()); // 68
  17. const myStringClass = new IdentityClass<string>("Semlinker!");
  18. console.log(myStringClass.getIdentity()); // Semlinker!

什么时候需要使用泛型?

当你的函数、接口或类将处理多种数据类型时

可能刚开始不使用泛型,随着项目越来越复杂,需要支持多种数据类型,你可能要考虑使用泛型了

类型约束,需预定义一个接口

希望限制每个类型变量(T)接受的类型数量

1 只允许函数传入那些包含 length 属性的变量

  1. interface Length {
  2. length: number
  3. }
  4. function logAdvance<T extends Length>(value: T): T {
  5. console.log(value, value.length);
  6. return value;
  7. }
  8. // 必须传入具有 length 属性的
  9. logAdvance([1])
  10. logAdvance('123')
  11. logAdvance({ length: 3 })

2 举一个例子,返回一个对象上的 k-v 值

  1. // 限制输入的 key 是对象上的
  2. function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  3. return obj[key];
  4. }
  5. let tsInfo = {
  6. name: "Typescript",
  7. supersetOf: "Javascript",
  8. }
  9. let difficulty = getProperty(tsInfo, 'supersetOf'); // OK

泛型参数指定默认类型

  1. interface A<T=string> {
  2. name: T;
  3. }
  4. const strA: A = { name: "Semlinker" };
  5. // 如果想传入 number,需写上<number>
  6. const numB: A<number> = { name: 101 };

泛型工具类型 - 映射类型

返回一个新类型

常用的如 Partial、Required、Readonly、Record 和 ReturnType

  1. /**
  2. * node_modules/typescript/lib/lib.es5.d.ts
  3. * Make all properties in T optional
  4. */
  5. type Partial<T> = {
  6. [P in keyof T]?: T[P];
  7. };
  8. /**
  9. * node_modules/typescript/lib/lib.es5.d.ts
  10. * Construct a type with a set of properties K of type T
  11. * 引入新的类型
  12. */
  13. type Record<K extends keyof any, T> = {
  14. [P in K]: T;
  15. };
  16. // node_modules/typescript/lib/lib.es5.d.ts
  17. /**
  18. * From T, pick a set of properties whose keys are in the union K
  19. * 把 T 的子属性挑出来,变成包含这个类型部分属性的子类型。
  20. */
  21. type Pick<T, K extends keyof T> = {
  22. [P in K]: T[P];
  23. };
  24. // node_modules/typescript/lib/lib.es5.d.ts
  25. /**
  26. * Exclude from T those types that are assignable to U
  27. * 将 T 中某些属于 U 的属性剔除掉
  28. *
  29. */
  30. type Exclude<T, U> = T extends U ? never : T;

类型声明和声明文件

为啥要写声明文件,不写编辑器就会报错,类型检查不通过,代码编译不通过。

声明文件的作用?
比如你引入一个 JS 文件,TS 编译器并不能知道这个模块导出了什么接口,所以你要写一个 .d.ts 来告诉 TS 你所暴露的成员都是什么,接受什么参数等等。

TypeScript 的声明文件是一个以 .d.ts 为后缀的 TypeScript 代码文件, 但它的作用是描述一个 JavaScript 模块(广义上的)内所有导出接口的类型信息

前置学习条件

  • 学习 declare 语句
  • 不同场景下,如何导出
    • 普通模块
    • 全局JS
    • UMD
  • 学习声明合并
  • 学习 namespace

引入的@types包
默认所有可见的”@types”包会在编译过程中被包含进来。如果指定了 typeRoots和types,结果会有些不同。看这里 https://www.tslang.cn/docs/handbook/tsconfig-json.html

我们结合常见的三种场景梳理一下类型声明文件该怎么写

全局 JS

修改全局作用域 的模块( 模块化引入 )

global-modifying-module.d.ts

当一个全局修改的模块被导入的时候,它们会改变全局作用域里的值

  1. // Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]
  2. // Project: [~THE PROJECT NAME~]
  3. // Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>
  4. /*~ This is the global-modifying module template file. You should rename it to index.d.ts
  5. *~ and place it in a folder with the same name as the module.
  6. *~ For example, if you were writing a file for "super-greeter", this
  7. *~ file should be 'super-greeter/index.d.ts'
  8. */
  9. /*~ Note: If your global-modifying module is callable or constructable, you'll
  10. *~ need to combine the patterns here with those in the module-class or module-function
  11. *~ template files
  12. */
  13. declare global {
  14. /*~ Here, declare things that go in the global namespace, or augment
  15. *~ existing declarations in the global namespace
  16. */
  17. interface String {
  18. fancyFormat(opts: StringFormatOptions): string;
  19. }
  20. }
  21. /*~ If your module exports types or values, write them as usual */
  22. export interface StringFormatOptions {
  23. fancinessLevel: number;
  24. }
  25. /*~ For example, declaring a method on the module (in addition to its global side effects) */
  26. export function doSomething(): void;
  27. /*~ If your module exports nothing, you'll need this line. Otherwise, delete it */
  28. export { };

修改全局作用域 的 JS 插件(通过 script 引入)

global-plugin.d.ts

  1. // Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]
  2. // Project: [~THE PROJECT NAME~]
  3. // Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>
  4. /*~ This template shows how to write a global plugin. */
  5. /*~ Write a declaration for the original type and add new members.
  6. *~ For example, this adds a 'toBinaryString' method with to overloads to
  7. *~ the built-in number type.
  8. */
  9. interface Number {
  10. toBinaryString(opts?: MyLibrary.BinaryFormatOptions): string;
  11. toBinaryString(callback: MyLibrary.BinaryFormatCallback, opts?: MyLibrary.BinaryFormatOptions): string;
  12. }
  13. /*~ If you need to declare several types, place them inside a namespace
  14. *~ to avoid adding too many things to the global namespace.
  15. 放到命名空间下,避免太多全局变量
  16. */
  17. declare namespace MyLibrary {
  18. type BinaryFormatCallback = (n: number) => string;
  19. interface BinaryFormatOptions {
  20. prefix?: string;
  21. padding: number;
  22. }
  23. }

普通的全局库( script 引入)

使用 global.d.ts 模板

  1. // Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]
  2. // Project: [~THE PROJECT NAME~]
  3. // Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>
  4. /*~ If this library is callable (e.g. can be invoked as myLib(3)),
  5. 如果该库可调用 muLib(3)
  6. *~ include those call signatures here.
  7. *~ Otherwise, delete this section.
  8. */
  9. declare function myLib(a: string): string;
  10. declare function myLib(a: number): number;
  11. /*~ If you want the name of this library to be a valid type name,
  12. *~ you can do so here.
  13. 想让你的类库是一个有效的类型名
  14. *~
  15. *~ For example, this allows us to write 'var x: myLib';
  16. *~ Be sure this actually makes sense! If it doesn't, just
  17. *~ delete this declaration and add types inside the namespace below.
  18. */
  19. interface myLib {
  20. name: string;
  21. length: number;
  22. extras?: string[];
  23. }
  24. /*~ If your library has properties exposed on a global variable,
  25. 你的类库在全局变量下暴露的很多属性
  26. *~ place them here.
  27. *~ You should also place types (interfaces and type alias) here.
  28. */
  29. declare namespace myLib {
  30. //~ We can write 'myLib.timeout = 50;'
  31. let timeout: number;
  32. //~ We can access 'myLib.version', but not change it
  33. const version: string;
  34. //~ There's some class we can create via 'let c = new myLib.Cat(42)'
  35. //~ Or reference e.g. 'function f(c: myLib.Cat) { ... }
  36. class Cat {
  37. constructor(n: number);
  38. //~ We can read 'c.age' from a 'Cat' instance
  39. readonly age: number;
  40. //~ We can invoke 'c.purr()' from a 'Cat' instance
  41. purr(): void;
  42. }
  43. //~ We can declare a variable as
  44. //~ 'var s: myLib.CatSettings = { weight: 5, name: "Maru" };'
  45. interface CatSettings {
  46. weight: number;
  47. name: string;
  48. tailLength?: number;
  49. }
  50. //~ We can write 'const v: myLib.VetID = 42;'
  51. //~ or 'const v: myLib.VetID = "bob";'
  52. type VetID = string | number;
  53. //~ We can invoke 'myLib.checkCat(c)' or 'myLib.checkCat(c, v);'
  54. function checkCat(c: Cat, s?: VetID);
  55. }

UMD

UMD 指的是既可以通过 script 引入,通过全局变量执行,又可以通过模块化引入的方式。

为什么模块导出要这样写?export = MyClass;
TODO;

如果模块能够作为 function调用
module-function.d.ts

  1. export as namespace myFuncLib;
  2. /*~ This declaration specifies that the function
  3. *~ is the exported object from the file
  4. */
  5. export = MyFunction;
  6. /*~ This example shows how to have multiple overloads for your function */
  7. declare function MyFunction(name: string): MyFunction.NamedReturnType;
  8. declare function MyFunction(length: number): MyFunction.LengthReturnType;
  9. /*~ If you want to expose types from your module as well, you can
  10. *~ place them in this block. Often you will want to describe the
  11. *~ shape of the return type of the function; that type should
  12. *~ be declared in here, as this example shows.
  13. */
  14. declare namespace MyFunction {
  15. export interface LengthReturnType {
  16. width: number;
  17. height: number;
  18. }
  19. export interface NamedReturnType {
  20. firstName: string;
  21. lastName: string;
  22. }
  23. /*~ If the module also has properties, declare them here. For example,
  24. *~ this declaration says that this code is legal:
  25. *~ import f = require('myFuncLibrary');
  26. *~ console.log(f.defaultName);
  27. */
  28. export const defaultName: string;
  29. export let defaultLength: number;
  30. }

如果模块能够被 new
module-class.d.ts

  1. export as namespace myClassLib;
  2. /*~ This declaration specifies that the class constructor function
  3. *~ is the exported object from the file
  4. */
  5. export = MyClass;
  6. /*~ Write your module's methods and properties in this class */
  7. declare class MyClass {
  8. constructor(someParam?: string);
  9. someProperty: string[];
  10. myMethod(opts: MyClass.MyClassMethodOptions): number;
  11. }
  12. /*~ If you want to expose types from your module as well, you can
  13. *~ place them in this block.
  14. */
  15. declare namespace MyClass {
  16. export interface MyClassMethodOptions {
  17. width?: number;
  18. height?: number;
  19. }
  20. }

如果模块不能被调用或构造
module.d.ts

下面是 module.d.ts 的例子

  1. // Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]
  2. // Project: [~THE PROJECT NAME~]
  3. // Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>
  4. /*~ This is the module template file. You should rename it to index.d.ts
  5. *~ and place it in a folder with the same name as the module.
  6. *~ For example, if you were writing a file for "super-greeter", this
  7. *~ file should be 'super-greeter/index.d.ts'
  8. */
  9. /*~ If this module is a UMD module that exposes a global variable 'myLib' when
  10. *~ loaded outside a module loader environment, declare that global here.
  11. *~ Otherwise, delete this declaration.
  12. */
  13. export as namespace myLib;
  14. /*~ If this module has methods, declare them as functions like so.
  15. */
  16. export function myMethod(a: string): string;
  17. export function myOtherMethod(a: number): number;
  18. /*~ You can declare types that are available via importing the module */
  19. // 您可以通过导入模块声明可用的类型
  20. // 这些类型声明可以在你的代码库中使用
  21. export interface someType {
  22. name: string;
  23. length: number;
  24. extras?: string[];
  25. }
  26. /*~ You can declare properties of the module using const, let, or var */
  27. export const myField: number;
  28. /*~ If there are types, properties, or methods inside dotted names
  29. *~ of the module, declare them inside a 'namespace'.
  30. */
  31. export namespace subProp {
  32. /*~ For example, given this definition, someone could write:
  33. *~ import { subProp } from 'yourModule';
  34. *~ subProp.foo();
  35. *~ or
  36. *~ import * as yourMod from 'yourModule';
  37. *~ yourMod.subProp.foo();
  38. */
  39. export function foo(): void;
  40. }

插件

插件的引入,会改变模块或者全局变量

模块插件或者 UMD 插件

module-plugin.d.ts

比如一个 moment 的插件,会为 moment 对象添加新的方法

了解一下 declare module 扩展模块 的用法

  1. // Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]
  2. // Project: [~THE PROJECT NAME~]
  3. // Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>
  4. /*~ This is the module plugin template file. You should rename it to index.d.ts
  5. *~ and place it in a folder with the same name as the module.
  6. *~ For example, if you were writing a file for "super-greeter", this
  7. *~ file should be 'super-greeter/index.d.ts'
  8. */
  9. /*~ On this line, import the module which this module adds to */
  10. import * as m from 'someModule';
  11. /*~ You can also import other modules if needed */
  12. import * as other from 'anotherModule';
  13. /*~ Here, declare the same module as the one you imported above */
  14. declare module 'someModule' {
  15. /*~ Inside, add new function, classes, or variables. You can use
  16. *~ unexported types from the original module if needed. */
  17. export function theNewMethod(x: m.foo): other.bar;
  18. /*~ You can also add new properties to existing interfaces from
  19. *~ the original module by writing interface augmentations */
  20. export interface SomeModuleOptions {
  21. someModuleSetting?: string;
  22. }
  23. /*~ New types can also be declared and will appear as if they
  24. *~ are in the original module */
  25. export interface MyModulePluginOptions {
  26. size: number;
  27. }
  28. }

模块

如果我们一个 JS 库,想要发布到 npm 上,并且像为它添加上类型文件。可以直接放一个 index.d.ts 文件(位置在包的根目录下,与 index.js 并列 ),就行了。或者不叫 index.d.ts ,那样就需要通过 package.json 文件的 typings 或者 types 字段,指定这个文件。我们来看一个例子

  1. {
  2. "name": "awesome",
  3. "author": "Vandelay Industries",
  4. "version": "1.0.0",
  5. "main": "./lib/main.js",
  6. "types": "./lib/main.d.ts"
  7. }

如果我们引入别人写的模块,而且正好 @types仓库里没有现成的,那自己整一个吧。

创建一个 types 目录,把声明文件都放到这个目录下,然后修改 tsconfig.json

  1. {
  2. "compilerOptions": {
  3. "module": "commonjs",
  4. "baseUrl": "./",
  5. "paths": {
  6. "*": ["types/*"]
  7. }
  8. }
  9. }

把上面 UMD 那个示例文件,删掉下面这一行,不用导出全局变量

export as namespace myLib;

关于依赖

如果我们写的类库中依赖的第三方库,没有将自己的声明文件放到包中,那我们需要下载该依赖文件,并且依赖项放到 dependencies,而不是 devDependencies。如果放到 dev 依赖,用户就需要自己 install @types/xxx 了。

  1. {
  2. "name": "browserify-typescript-extension",
  3. "author": "Vandelay Industries",
  4. "version": "1.0.0",
  5. "main": "./lib/main.js",
  6. "types": "./lib/main.d.ts",
  7. "dependencies": {
  8. "browserify": "latest",
  9. "@types/browserify": "latest",
  10. "typescript": "next"
  11. }
  12. }

全局变量

下面的写法用到了声明合并,命名空间和函数合并,相当于为函数添加属性和方法

  1. // global.d.ts
  2. declare function globalLib(options: globalLib.Options): void;
  3. declare namespace globalLib {
  4. const version: string;
  5. function doSomething(): void;
  6. interface Options {
  7. [key: string]: any
  8. }
  9. }
  10. // global.ts
  11. function globalLib(options) {
  12. console.log(options);
  13. }
  14. globalLib.version = '1.0.0';
  15. globalLib.doSomething = function() {
  16. console.log('globalLib do something');
  17. };

自动生成声明文件

如果你的类库是用 TS 写的,在 tsconfig.json 里开启 declaration 选型,或者使用 tsc —declaration

  1. {
  2. "compilerOptions": {
  3. "module": "commonjs",
  4. "outDir": "lib",
  5. "declaration": true,
  6. }
  7. }

类型别名

  1. type Message = string | string[];

interface

工程知识

常用的 npm 依赖

  1. "dependencies": {
  2. "typescript": "3.5.3"
  3. },
  4. "devDependencies": {
  5. "@types/jest": "24.0.15",
  6. "@types/node": "12.6.8",
  7. "@types/react": "16.8.23",
  8. "@types/react-dom": "16.8.5",
  9. "@types/lodash": "^4.14.136",
  10. "@types/react-redux": "^7.1.2",
  11. "@types/react-router-dom": "^4.3.4",
  12. }

@types放哪里的问题

假设您正在开发一个包含“A”的包,它在devDependencies中包含@types/some-module包。出于某种原因,您要从@types/some-module导出类型

  1. import {SomeType} from 'some-module';
  2. export default class APackageClass {
  3. constructor(private config: SomeType) {
  4. }
  5. }

现在,包“A”的TypeScript使用者无法猜出SomeType是什么,因为未安装包“A”的devDependencies。

在这种特殊情况下,您需要将@ types/*包与常规“依赖”放在一起。对于其他情况,“devDependencies”足够好。

node 执行 ts 代码

node 环境需要使用 ts-node 来编译 ts 代码

编译工具、代码检查工具

前端常用的 TS 知识 - 图1

配置文件

  1. {
  2. "compilerOptions": {
  3. /* 基本选项 */
  4. "target": "es5", // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
  5. "module": "commonjs", // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
  6. "lib": [], // 指定要包含在编译中的库文件
  7. "allowJs": true, // 允许编译 javascript 文件
  8. "checkJs": true, // 报告 javascript 文件中的错误
  9. "jsx": "preserve", // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
  10. "declaration": true, // 生成相应的 '.d.ts' 文件
  11. "sourceMap": true, // 生成相应的 '.map' 文件
  12. "outFile": "./", // 将输出文件合并为一个文件
  13. "outDir": "./", // 指定输出目录
  14. "rootDir": "./", // 用来控制输出目录结构 --outDir.
  15. "removeComments": true, // 删除编译后的所有的注释
  16. "noEmit": true, // 不生成输出文件
  17. "importHelpers": true, // 从 tslib 导入辅助工具函数
  18. "isolatedModules": true, // 将每个文件做为单独的模块 (与 'ts.transpileModule' 类似).
  19. /* 严格的类型检查选项 */
  20. "strict": true, // 启用所有严格类型检查选项
  21. "noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错
  22. "strictNullChecks": true, // 启用严格的 null 检查
  23. "noImplicitThis": true, // 当 this 表达式值为 any 类型的时候,生成一个错误
  24. "alwaysStrict": true, // 以严格模式检查每个模块,并在每个文件里加入 'use strict'
  25. /* 额外的检查 */
  26. "noUnusedLocals": true, // 有未使用的变量时,抛出错误
  27. "noUnusedParameters": true, // 有未使用的参数时,抛出错误
  28. "noImplicitReturns": true, // 并不是所有函数里的代码都有返回值时,抛出错误
  29. "noFallthroughCasesInSwitch": true, // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)
  30. /* 模块解析选项 */
  31. "moduleResolution": "node", // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
  32. "baseUrl": "./", // 用于解析非相对模块名称的基目录
  33. "paths": {}, // 模块名到基于 baseUrl 的路径映射的列表
  34. "rootDirs": [], // 根文件夹列表,其组合内容表示项目运行时的结构内容
  35. "typeRoots": [], // 包含类型声明的文件列表
  36. "types": [], // 需要包含的类型声明文件名列表
  37. "allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入。
  38. /* Source Map Options */
  39. "sourceRoot": "./", // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
  40. "mapRoot": "./", // 指定调试器应该找到映射文件而不是生成文件的位置
  41. "inlineSourceMap": true, // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
  42. "inlineSources": true, // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性
  43. /* 其他选项 */
  44. "experimentalDecorators": true, // 启用装饰器
  45. "emitDecoratorMetadata": true // 为装饰器提供元数据的支持
  46. }
  47. }

参考

https://mp.weixin.qq.com/s/zM_tUbmrfys67voLWWCDMA