TypeScript 版本特性及技巧

2020-09-02  本文已影响0人  风之化身呀

一、版本特性

4.1

type World = "world";
type Greeting = `hello ${World}`;

// example1
type VerticalAlignment = "top" | "middle" | "bottom";
type HorizontalAlignment = "left" | "center" | "right";
declare function setAlignment(value: `${VerticalAlignment}-${HorizontalAlignment}`): void;

// example2
let person = makeWatchedObject({
  firstName: "Homer",
  age: 42, // give-or-take
  location: "Springfield",
});
person.on("firstNameChanged", () => {
  console.log(`firstName was changed!`);
});

type PropEventSource<T> = {
    on(eventName: `${string & keyof T}Changed`, callback: () => void): void;
};
declare function makeWatchedObject<T>(obj: T): T & PropEventSource<T>;
// 新语法 k in keyof T as NewKeyType
type MappedTypeWithNewKeys<T> = {
    [K in keyof T as NewKeyType]: T[K]
}
// example1
type Getters<T> = {
    [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};
interface Person {
    name: string;
    age: number;
    location: string;
}

type LazyPerson = Getters<Person>;
// equal to
type LazyPerson = {
  getName: () => string;
  getAge: () => number;
  getLocation: () => string;
}

// example2
type RemoveKindField<T> = {
    [K in keyof T as Exclude<K, "kind">]: T[K]
};
interface Circle {
    kind: "circle";
    radius: number;
}
type KindlessCircle = RemoveKindField<Circle>;
type ElementType<T> = T extends ReadonlyArray<infer U> ? ElementType<U> : T;

function deepFlatten<T extends readonly unknown[]>(x: T): ElementType<T>[] {
  throw "not implemented";
}

// All of these return the type 'number[]':
deepFlatten([1, 2, 3]);
deepFlatten([[1], [2, 3]]);
deepFlatten([[1], [[2]], [[[3]]]]);
interface Options {
  path: string;
  permissions: number;
  [propName: string]: string | number;
}

const opt:Options
opt.xxx  string|number|undefined

// 影响 for 循环,不影响for-of,forEach
function screamLines(strs: string[]) {
  // This will have issues
  for (let i = 0; i < strs.length; i++) {
    console.log(strs[i].toUpperCase());
Object is possibly 'undefined'.
  }
}
// resolve’s Parameters Are No Longer Optional in Promises
new Promise((resolve) => {
  resolve() // error
});

// fix 
new Promise<void>((resolve) => {
  resolve() // work
});

4.0

type IT = [string,boolean]
function tail<T extends any[]>(arr: readonly [any, ...IT]) {
  const [_ignored, ...rest] = arr;
  return rest;
}

type Strings = [string, string];
type Numbers = [number, number];
type StrStrNumNumBool = [...Strings, ...Numbers, boolean];

// 不限参数长度
type Strings = [string, string];
type Numbers = number[];
type Unbounded = [...Strings, ...Numbers, boolean];
//   ==> type Unbounded = [string, string, ...(number | boolean)[]]
function foo(...args: [string, number]): void {
  // ...
}

是这种提示:



而这种写法的提示语意上更为明确

type IRange = [start: string, end: number];
function foo(...args: IRange): void {
  // ...
}
foo()
if(!a){
   a=b
}
===>
a||=b

3.9

interface Lion {
  roar(): void;
}
interface Seal {
  singKissFromARose(): void;
}

async function visitZoo(
  lionExhibit: Promise<Lion>,
  sealExhibit: Promise<Seal | undefined>
) {
  let [lion, seal] = await Promise.all([lionExhibit, sealExhibit]);
  lion.roar(); // uh oh
  // lion is possibly 'undefined'.
}
// before, this transform is a bug
foo?.bar!.baz;  =>  (foo?.bar).baz;  
// now
foo?.bar!.baz;  =>  foo?.bar.baz;  
interface A {
  a: number; // notice this is 'number'
}
interface B {
  b: string;
}
interface C {
  a?: boolean; // notice this is 'boolean'
  b: string;
}
declare let x: A & B;
declare let y: C;
// before is ok , now is error
y = x;
function foo<T extends any>(arg: T) {
  arg.spfjgerijghoied; // before is ok , now is  error!
}

3.x

function foo(arr: ReadonlyArray<string>) {
  arr.slice(); // okay
  arr.push("hello!"); // error!
}

也可写成 readonly string[],同时也支持 tuples 类型:readonly [string,number]

// Type '"hello"'
let x = "hello" as const;

// Type 'readonly [10, 20]'
let y = [10, 20] as const;

// Type '{ readonly text: "hello" }'
let z = { text: "hello" } as const;
type Person = {
  name: string;
  age: number;
  location: string;
};

type QuantumPerson = Omit<Person, "location">;

// equivalent to
type QuantumPerson = {
  name: string;
  age: number;
};
// Before
if (foo && foo.bar && foo.bar.baz) {
  // ...
}
// After
if (foo?.bar?.baz) {
  // ...
}
let x = foo ?? bar();
// equal to
let x = foo !== null && foo !== undefined ? foo : bar();

注意和 || 运算不同,|| 包含了更多 falsy 类的值,如 false,'',0 等

import type { SomeThing } from "./some-module.js";
export type { SomeThing };
class C {
  #foo = 10;
  cHelper() {
    return this.#foo;
  }
}

二、小技巧

enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green; // 2
let colorName: string = Color[Color.Green];  // 'Green'
type Cb = ()=>void
// bad
interface IState{
      loading: boolean;
      data:any[];
}
const defalutState:IState={
    loading:true,
    data:[]
}
// good
const defalutState = {
    loading:true,
    data:[]
}
type IState = typeof defalutState
declare module "*!text" {
    const content: string;
    export default content;
}
// Some do it the other way around.
declare module "json!*" {
    const value: any;
    export default value;
}

import fileContent from "./xyz.txt!text";
import data from "json!http://example.com/data.json";
interface Person {
  addr: {
    city: string,
    street: string,
    num: number,
  }
}

当需要使用 addr 的类型时, 可以直接 Person["addr"]

abstract class Animal extends React.PureComponent {
  /* Common methods here. */
}
class Cat extends Animal {}
class Dog extends Animal {}

// `AnimalComponent` must be a class of Animal.
const renderAnimal = (AnimalComponent: Animal) => {
  return <AnimalComponent/>; // WRONG!
}

上面的代码是错的,因为 Animal 是实例类型,不是类本身。应该

interface ClassOf<T> {
  new (...args: any[]): T;
}
const renderAnimal = (AnimalComponent: ClassOf<Animal>) => {
  return <AnimalComponent/>; // Good!
}

renderAnimal(Cat); // Good!
renderAnimal(Dog); // Good!

首先我们需要明确包使用的导出规范,global/umd/commonjs/module 等
对于 global 导出的包我们使用:

declare namesapce MyLib {
  class A {}
  
  // 我们可以直接在代码中使用
  // const a = new MyLib.A()
}

对于 umd/commonjs 导出的包我们使用:

declare module 'my-lib' {
  namespace MyLib {
    class A {}
    
    class B {}

    // 使用时
    // 我们可以使用
    // import * as MyLib from 'my-lib'
    // const a = new MyLib.A();

    // 如果开启了 ES Module 融合模式 (esModuleInterop=true)
    // 我们可以使用
    // import { A } from 'my-lib'
    // const a = new A()
  }
  export = MyLib
}

对于 ES Module 导出的包我们使用:

declare module 'my-lib' {
  class MyLib {}
  
  export default MyLib
  
  // or other exorts
  export class A {}
  
  // 我们可以使用
  // import MyLib, {A} from 'my-lib'
  // const lib = new MyLib()
  // const a = new A()
}
export const enum T{
    a=1
}
T.a  // 会被编译为 1 /*a*/

enum T{
    a=1
}
T.a 会被编译为
var ITest;
(function (ITest) {
    ITest[ITest["a"] = 1] = "a";
})(ITest || (ITest = {}));
ITest.a
{
  "compilerOptions": {

    /* 基本选项 */
    "target": "es5",                       // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
    "module": "commonjs",                  // 指定使用模块: 'commonjs', 'amd', 'system', 'umd' or 'es2015'
    "lib": [],                             // 指定要包含在编译中的库文件
    "allowJs": true,                       // 允许编译 javascript 文件
    "checkJs": true,                       // 报告 javascript 文件中的错误
    "jsx": "preserve",                     // 指定 jsx 代码的生成: 'preserve', 'react-native', or 'react'
    "declaration": true,                   // 生成相应的 '.d.ts' 文件
    "sourceMap": true,                     // 生成相应的 '.map' 文件
    "outFile": "./",                       // 将输出文件合并为一个文件
    "outDir": "./",                        // 指定输出目录
    "rootDir": "./",                       // 用来控制输出目录结构 --outDir.
    "removeComments": true,                // 删除编译后的所有的注释
    "noEmit": true,                        // 不生成输出文件
    "importHelpers": true,                 // 从 tslib 导入辅助工具函数
    "isolatedModules": true,               // 将每个文件做为单独的模块 (与 'ts.transpileModule' 类似).

    /* 严格的类型检查选项 */
    "strict": true,                        // 启用所有严格类型检查选项
    "noImplicitAny": true,                 // 在表达式和声明上有隐含的 any类型时报错
    "strictNullChecks": true,              // 启用严格的 null 检查
    "noImplicitThis": true,                // 当 this 表达式值为 any 类型的时候,生成一个错误
    "alwaysStrict": true,                  // 以严格模式检查每个模块,并在每个文件里加入 'use strict'

    /* 额外的检查 */
    "noUnusedLocals": true,                // 有未使用的变量时,抛出错误
    "noUnusedParameters": true,            // 有未使用的参数时,抛出错误
    "noImplicitReturns": true,             // 并不是所有函数里的代码都有返回值时,抛出错误
    "noFallthroughCasesInSwitch": true,    // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)

    /* 模块解析选项 */
    "moduleResolution": "node",            // 选择模块解析策略: 'node' (Node.js) or 'classic' (TypeScript pre-1.6)
    "baseUrl": "./",                       // 用于解析非相对模块名称的基目录
    "paths": {},                           // 模块名到基于 baseUrl 的路径映射的列表
    "rootDirs": [],                        // 根文件夹列表,其组合内容表示项目运行时的结构内容
    "typeRoots": [],                       // 包含类型声明的文件列表
    "types": [],                           // 需要包含的类型声明文件名列表
    "allowSyntheticDefaultImports": true,  // 允许从没有设置默认导出的模块中默认导入。

    /* Source Map Options */
    "sourceRoot": "./",                    // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
    "mapRoot": "./",                       // 指定调试器应该找到映射文件而不是生成文件的位置
    "inlineSourceMap": true,               // 生成单个 soucemaps 文件,而不是将 sourcemaps 生成不同的文件
    "inlineSources": true,                 // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性

    /* 其他选项 */
    "experimentalDecorators": true,        // 启用装饰器
    "emitDecoratorMetadata": true          // 为装饰器提供元数据的支持

    /* 编译文件选择 */
    "files":[],
    "include":[],
    "exclude":[],
  }
}

参考

上一篇下一篇

猜你喜欢

热点阅读