前端

前端基础 03、TypeScript 入门

2022-02-10  本文已影响0人  yjtuuige

一、TypeScript 概述

1. TypeScript 与 JavaScript 的区别
TypeScript JavaScript
JavaScript 的超集,解决代码复杂性 脚本语言,用于创建动态网页
可以在编译期间发现并纠正错误 解释型语言,只能在运行时发现错误
强类型,支持静态和动态类型 弱类型,没有静态类型选项
最终编译成 JS 代码,浏览器才可使用 可以直接在浏览器中使用
支持模块、泛型和接口 不支持模块,泛型或接口
社区的支持仍在增长,还不是很大 大量的社区支持以及大量文档和解决问题的支持
2. 获取 TypeScript

命令行的 TypeScript 编译器,使用 npm 包管理器安装:

$ npm install -g typescript
$ tsc -v 
# Version 4.0.2
$ tsc hello.ts
# hello.ts => hello.js
3. TypeScript 工作流程
4. TypeScript 测试
function greet(person: string) {
  return 'Hello, ' + person;
}

console.log(greet("TypeScript"));
"use strict";
function greet(person) {
    return 'Hello, ' + person;
}
console.log(greet("TypeScript"));

二、TypeScript 基础类型

1. Boolean 类型
let isDone: boolean = false;
// ES5:var isDone = false;
2. Number 类型
let count: number = 10;
// ES5:var count = 10;
3.String 类型
let name: string = "semliker";
// ES5:var name = 'semlinker';
4. Symbol 类型
let sym = Symbol();
let obj = {
    [sym]: "value"
};

console.log(obj[sym]); // "value"
5. Array 类型
let list: number[] = [1, 2, 3];
// ES5:var list = [1,2,3];

let list: Array<number> = [1, 2, 3]; // Array<number>泛型语法
// ES5:var list = [1,2,3];
6. Enum 类型
  1. 数字枚举
enum Direction {
  NORTH,
  SOUTH,
  EAST,
  WEST,
}

let dir: Direction = Direction.NORTH;
var Direction;
(function (Direction) {
    Direction[Direction["NORTH"] = 0] = "NORTH";
    Direction[Direction["SOUTH"] = 1] = "SOUTH";
    Direction[Direction["EAST"] = 2] = "EAST";
    Direction[Direction["WEST"] = 3] = "WEST";
})(Direction || (Direction = {}));
var dir = Direction.NORTH;
enum Direction {
  NORTH = 3,
  SOUTH,
  EAST,
  WEST,
}
  1. 字符串枚举
enum Direction {
  NORTH = "NORTH",
  SOUTH = "SOUTH",
  EAST = "EAST",
  WEST = "WEST",
}
var Direction;
(function (Direction) {
    Direction["NORTH"] = "NORTH";
    Direction["SOUTH"] = "SOUTH";
    Direction["EAST"] = "EAST";
    Direction["WEST"] = "WEST";
})(Direction || (Direction = {}));
enum Direction {
  NORTH,
  SOUTH,
  EAST,
  WEST,
}

let dirName = Direction[0]; // NORTH
let dirVal = Direction["NORTH"]; // 0
  1. 常量枚举
const enum Direction {
  NORTH,
  SOUTH,
  EAST,
  WEST,
}

let dir: Direction = Direction.NORTH;
var dir = 0 /* NORTH */;
  1. 异构枚举

异构枚举的成员值是数字和字符串的混合:

enum Enum {
  A,
  B,
  C = "C",
  D = "D",
  E = 8,
  F,
}
var Enum;
(function (Enum) {
    Enum[Enum["A"] = 0] = "A";
    Enum[Enum["B"] = 1] = "B";
    Enum["C"] = "C";
    Enum["D"] = "D";
    Enum[Enum["E"] = 8] = "E";
    Enum[Enum["F"] = 9] = "F";
})(Enum || (Enum = {}));
console.log(Enum.A) //输出:0
console.log(Enum[0]) // 输出:A
7. Any 类型
let notSure: any = 666;
notSure = "semlinker";
notSure = false;
let value: any;

value.foo.bar; // OK
value.trim(); // OK
value(); // OK
new value(); // OK
value[0][1]; // OK
8. Unknown 类型
let value: unknown;

value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {}; // OK
value = Math.random; // OK
value = null; // OK
value = undefined; // OK
value = new TypeError(); // OK
value = Symbol("type"); // OK
let value: unknown;

let value1: unknown = value; // OK
let value2: any = value; // OK
let value3: boolean = value; // Error
let value4: number = value; // Error
let value5: string = value; // Error
let value6: object = value; // Error
let value7: any[] = value; // Error
let value8: Function = value; // Error
let value: unknown;

value.foo.bar; // Error
value.trim(); // Error
value(); // Error
new value(); // Error
value[0][1]; // Error
9. Tuple 类型
let tupleType: [string, boolean];
tupleType = ["semlinker", true];
console.log(tupleType[0]); // semlinker
console.log(tupleType[1]); // true
tupleType = [true, "semlinker"];
[0]: Type 'true' is not assignable to type 'string'.
[1]: Type 'string' is not assignable to type 'boolean'.
tupleType = ["semlinker"];
Property '1' is missing in type '[string]' but required in type '[string, boolean]'.
10. Void 类型
// 声明函数返回值为void
function warnUser(): void {
  console.log("This is my warning message");
}
function warnUser() {
  console.log("This is my warning message");
}
let unusable: void = undefined;
11. Null 和 Undefined 类型
let u: undefined = undefined;
let n: null = null;
12. object、Object 和 {} 类型
  1. object 类型
// node_modules/typescript/lib/lib.es5.d.ts
interface ObjectConstructor {
  create(o: object | null): any;
  // ...
}

const proto = {};

Object.create(proto);     // OK
Object.create(null);      // OK
Object.create(undefined); // Error
Object.create(1337);      // Error
Object.create(true);      // Error
Object.create("oops");    // Error
  1. Object 类型
// node_modules/typescript/lib/lib.es5.d.ts
interface Object {
  constructor: Function;
  toString(): string;
  toLocaleString(): string;
  valueOf(): Object;
  hasOwnProperty(v: PropertyKey): boolean;
  isPrototypeOf(v: Object): boolean;
  propertyIsEnumerable(v: PropertyKey): boolean;
}
// node_modules/typescript/lib/lib.es5.d.ts
interface ObjectConstructor {
  /** Invocation via `new` */
  new(value?: any): Object;
  /** Invocation via function calls */
  (value?: any): any;
  readonly prototype: Object;
  getPrototypeOf(o: any): any;
  // ···
}

declare var Object: ObjectConstructor;
  1. {} 类型
// Type {}
const obj = {};

// Error: Property 'prop' does not exist on type '{}'.
obj.prop = "semlinker";
// Type {}
const obj = {};

// "[object Object]"
obj.toString();
13. Never 类型
// 返回never的函数必须存在无法达到的终点
function error(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {}
}
type Foo = string | number;

function controlFlowAnalysisWithNever(foo: Foo) {
  if (typeof foo === "string") {
    // 这里 foo 被收窄为 string 类型
  } else if (typeof foo === "number") {
    // 这里 foo 被收窄为 number 类型
  } else {
    // foo 在这里是 never
    const check: never = foo;
  }
}

三、TypeScript 断言

1. 类型断言

尖括号 语法

let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
  1. as 语法
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
2. 非空断言
const a: number | undefined = undefined;
const b: number = a!;
console.log(b); 
"use strict";
const a = undefined;
const b = a;
console.log(b);
3. 确定赋值断言
let x: number;
initialize();
// Variable 'x' is used before being assigned.(2454)
console.log(2 * x); // Error

function initialize() {
  x = 10;
}
let x!: number;
initialize();
console.log(2 * x); // Ok

function initialize() {
  x = 10;
}

四、类型守卫

1. in 关键字
interface Admin {
  name: string;
  privileges: string[];
}

interface Employee {
  name: string;
  startDate: Date;
}

type UnknownEmployee = Employee | Admin;

function printEmployeeInformation(emp: UnknownEmployee) {
  console.log("Name: " + emp.name);
  if ("privileges" in emp) {
    console.log("Privileges: " + emp.privileges);
  }
  if ("startDate" in emp) {
    console.log("Start Date: " + emp.startDate);
  }
}
2. typeof 关键字
function padLeft(value: string, padding: string | number) {
  if (typeof padding === "number") {
      return Array(padding + 1).join(" ") + value;
  }
  if (typeof padding === "string") {
      return padding + value;
  }
  throw new Error(`Expected string or number, got '${padding}'.`);
}
3. instanceof 关键字
interface Padder {
  getPaddingString(): string;
}

class SpaceRepeatingPadder implements Padder {
  constructor(private numSpaces: number) {}
  getPaddingString() {
    return Array(this.numSpaces + 1).join(" ");
  }
}

class StringPadder implements Padder {
  constructor(private value: string) {}
  getPaddingString() {
    return this.value;
  }
}

let padder: Padder = new SpaceRepeatingPadder(6);

if (padder instanceof SpaceRepeatingPadder) {
  // padder的类型收窄为 'SpaceRepeatingPadder'
}
4. 自定义类型保护的类型谓词
function isNumber(x: any): x is number {
  return typeof x === "number";
}

function isString(x: any): x is string {
  return typeof x === "string";
}

五、联合类型和类型别名

1. 联合类型
const sayHello = (name: string | undefined) => {
  /* ... */
};
sayHello("semlinker");
sayHello(undefined);
let num: 1 | 2 = 1;
type EventNames = 'click' | 'scroll' | 'mousemove';
2. 可辨识联合
  1. 可辨识
enum CarTransmission {
  Automatic = 200,
  Manual = 300
}

interface Motorcycle {
  vType: "motorcycle"; // discriminant
  make: number; // year
}

interface Car {
  vType: "car"; // discriminant
  transmission: CarTransmission
}

interface Truck {
  vType: "truck"; // discriminant
  capacity: number; // in tons
}
  1. 联合类型
type Vehicle = Motorcycle | Car | Truck;
  1. 类型守卫
const EVALUATION_FACTOR = Math.PI; 

function evaluatePrice(vehicle: Vehicle) {
  return vehicle.capacity * EVALUATION_FACTOR;
}

const myTruck: Truck = { vType: "truck", capacity: 9.5 };
evaluatePrice(myTruck);
Property 'capacity' does not exist on type 'Vehicle'.
Property 'capacity' does not exist on type 'Motorcycle
function evaluatePrice(vehicle: Vehicle) {
  switch(vehicle.vType) {
    case "car":
      return vehicle.transmission * EVALUATION_FACTOR;
    case "truck":
      return vehicle.capacity * EVALUATION_FACTOR;
    case "motorcycle":
      return vehicle.make * EVALUATION_FACTOR;
  }
}
3. 类型别名
type Message = string | string[];

let greet = (message: Message) => {
  // ...
};

六、交叉类型

type PartialPointX = { x: number; };
type Point = PartialPointX & { y: number; };

let point: Point = {
  x: 1,
  y: 1
}
1. 同名基础类型属性的合并
interface X {
  c: string;
  d: string;
}

interface Y {
  c: number;
  e: string
}

type XY = X & Y;
type YX = Y & X;

let p: XY;
let q: YX;
p = { c: 6, d: "d", e: "e" }; 
q = { c: "c", d: "d", e: "e" }; 
2. 同名非基础类型属性的合并
interface D { d: boolean; }
interface E { e: string; }
interface F { f: number; }

interface A { x: D; }
interface B { x: E; }
interface C { x: F; }

type ABC = A & B & C;

let abc: ABC = {
  x: {
    d: true,
    e: 'semlinker',
    f: 666
  }
};

console.log('abc:', abc);

七、TypeScript 函数

1. TypeScript 函数与 JavaScript 函数的区别
TypeScript JavaScript
含有类型 无类型
箭头函数 箭头函数(ES2015)
函数类型 无函数类型
必填和可选参数 所有参数都是可选的
默认参数 默认参数
剩余参数 剩余参数
函数重载 无函数重载
2. 箭头函数
myBooks.forEach(() => console.log('reading'));

myBooks.forEach(title => console.log(title));

myBooks.forEach((title, idx, arr) =>
  console.log(idx + '-' + title);
);

myBooks.forEach((title, idx, arr) => {
  console.log(idx + '-' + title);
});
2.使用示例
// 未使用箭头函数
function Book() {
  let self = this;
  self.publishDate = 2016;
  setInterval(function () {
    console.log(self.publishDate);
  }, 1000);
}

// 使用箭头函数
function Book() {
  this.publishDate = 2016;
  setInterval(() => {
    console.log(this.publishDate);
  }, 1000);
}
3. 参数类型和返回类型
function createUserId(name: string, id: number): string {
  return name + id;
}
4. 函数类型
let IdGenerator: (chars: string, nums: number) => string;

function createUserId(name: string, id: number): string {
  return name + id;
}

IdGenerator = createUserId;
5. 可选参数及默认参数
// 可选参数
function createUserId(name: string, id: number, age?: number): string {
  return name + id;
}

// 默认参数
function createUserId(
  name: string = "semlinker",
  id: number,
  age?: number
): string {
  return name + id;
}
6. 剩余参数
function push(array, ...items) {
  items.forEach(function (item) {
    array.push(item);
  });
}

let a = [];
push(a, 1, 2, 3);
7. 函数重载
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: string, b: number): string;
function add(a: number, b: string): string;
function add(a: Combinable, b: Combinable) {
  // type Combinable = string | number;
  if (typeof a === 'string' || typeof b === 'string') {
    return a.toString() + b.toString();
  }
  return a + b;
}
class Calculator {
  add(a: number, b: number): number;
  add(a: string, b: string): string;
  add(a: string, b: number): string;
  add(a: number, b: string): string;
  add(a: Combinable, b: Combinable) {
  if (typeof a === 'string' || typeof b === 'string') {
    return a.toString() + b.toString();
  }
    return a + b;
  }
}

const calculator = new Calculator();
const result = calculator.add('Semlinker', ' Kakuqo');

八、TypeScript 数组

1. 数组解构
let x: number; let y: number; let z: number;
let five_array = [0,1,2,3,4];
[x,y,z] = five_array;
2. 数组展开运算符
let two_array = [0, 1];
let five_array = [...two_array, 2, 3, 4];
3. 数组遍历
let colors: string[] = ["red", "green", "blue"];
for (let i of colors) {
  console.log(i);
}

九、TypeScript 对象

1. 对象解构
let person = {
  name: "Semlinker",
  gender: "Male",
};

let { name, gender } = person;
2. 对象展开运算符
let person = {
  name: "Semlinker",
  gender: "Male",
  address: "Xiamen",
};

// 组装对象
let personWithAge = { ...person, age: 33 };

// 获取除了某些项外的其它项
let { name, ...rest } = person;

十、TypeScript 接口

1. 对象的形状
interface Person {
  name: string;
  age: number;
}

let semlinker: Person = {
  name: "semlinker",
  age: 33,
};
2. 可选 | 只读属性
interface Person {
  readonly name: string;
  age?: number;
}
let a: number[] = [1, 2, 3, 4];
let ro: ReadonlyArray<number> = a;
ro[0] = 12; // error!
ro.push(5); // error!
ro.length = 100; // error!
a = ro; // error!
3. 任意属性
interface Person {
  name: string;
  age?: number;
  [propName: string]: any;
}

const p1 = { name: "semlinker" };
const p2 = { name: "lolo", age: 5 };
const p3 = { name: "kakuqo", sex: 1 }
4. 接口与类型别名的区别
  1. Objects/Functions
interface Point {
  x: number;
  y: number;
}

interface SetPoint {
  (x: number, y: number): void;
}
type Point = {
  x: number;
  y: number;
};

type SetPoint = (x: number, y: number) => void;
  1. Other Types
// primitive
type Name = string;

// object
type PartialPointX = { x: number; };
type PartialPointY = { y: number; };

// union
type PartialPoint = PartialPointX | PartialPointY;

// tuple
type Data = [number, string];
  1. Extend
interface PartialPointX { x: number; }
interface Point extends PartialPointX { 
  y: number; 
}
type PartialPointX = { x: number; };
type Point = PartialPointX & { y: number; };
type PartialPointX = { x: number; };
interface Point extends PartialPointX { y: number; }
interface PartialPointX { x: number; }
type Point = PartialPointX & { y: number; };
  1. Implements
interface Point {
  x: number;
  y: number;
}

class SomePoint implements Point {
  x = 1;
  y = 2;
}

type Point2 = {
  x: number;
  y: number;
};

class SomePoint2 implements Point2 {
  x = 1;
  y = 2;
}

type PartialPoint = { x: number; } | { y: number; };

// A class can only implement an object type or 
// intersection of object types with statically known members.
class SomePartialPoint implements PartialPoint { // Error
  x = 1;
  y = 2;
}
  1. Declaration merging
interface Point { x: number; }
interface Point { y: number; }

const point: Point = { x: 1, y: 2 };

十一、TypeScript 类

1. 类的属性与方法
class Greeter {
  // 静态属性
  static cname: string = "Greeter";
  // 成员属性
  greeting: string;

  // 构造函数 - 执行初始化操作
  constructor(message: string) {
    this.greeting = message;
  }

  // 静态方法
  static getClassName() {
    return "Class name is Greeter";
  }

  // 成员方法
  greet() {
    return "Hello, " + this.greeting;
  }
}

let greeter = new Greeter("world");
"use strict";
var Greeter = /** @class */ (function () {
    // 构造函数 - 执行初始化操作
    function Greeter(message) {
      this.greeting = message;
    }
    // 静态方法
    Greeter.getClassName = function () {
      return "Class name is Greeter";
    };
    // 成员方法
    Greeter.prototype.greet = function () {
      return "Hello, " + this.greeting;
    };
    // 静态属性
    Greeter.cname = "Greeter";
    return Greeter;
}());
var greeter = new Greeter("world");
2. ECMAScript 私有字段
class Person {
  #name: string;

  constructor(name: string) {
    this.#name = name;
  }

  greet() {
    console.log(`Hello, my name is ${this.#name}!`);
  }
}

let semlinker = new Person("Semlinker");

semlinker.#name;
//     ~~~~~
// Property '#name' is not accessible outside class 'Person'
// because it has a private identifier.
3. 访问器
let passcode = "Hello TypeScript";

class Employee {
  private _fullName: string;

  get fullName(): string {
    return this._fullName;
  }

  set fullName(newName: string) {
    if (passcode && passcode == "Hello TypeScript") {
      this._fullName = newName;
    } else {
      console.log("Error: Unauthorized update of employee!");
    }
  }
}

let employee = new Employee();
employee.fullName = "Semlinker";
if (employee.fullName) {
  console.log(employee.fullName);
}
4. 类的继承
class Animal {
  name: string;

  constructor(theName: string) {
    this.name = theName;
  }

  move(distanceInMeters: number = 0) {
    console.log(`${this.name} moved ${distanceInMeters}m.`);
  }
}

class Snake extends Animal {
  constructor(name: string) {
    super(name); // 调用父类的构造函数
  }

  move(distanceInMeters = 5) {
    console.log("Slithering...");
    super.move(distanceInMeters);
  }
}

let sam = new Snake("Sammy the Python");
sam.move();
5. 抽象类
abstract class Person {
  constructor(public name: string){}

  abstract say(words: string) :void;
}

// Cannot create an instance of an abstract class.(2511)
const lolo = new Person(); // Error
abstract class Person {
  constructor(public name: string){}

  // 抽象方法
  abstract say(words: string) :void;
}

class Developer extends Person {
  constructor(name: string) {
    super(name);
  }

  say(words: string): void {
    console.log(`${this.name} says ${words}`);
  }
}

const lolo = new Developer("lolo");
lolo.say("I love ts!"); // lolo says I love ts!
6. 类方法重载
class ProductService {
    getProducts(): void;
    getProducts(id: number): void;
    getProducts(id?: number) {
      if(typeof id === 'number') {
          console.log(`获取id为 ${id} 的产品信息`);
      } else {
          console.log(`获取所有的产品信息`);
      }  
    }
}

const productService = new ProductService();
productService.getProducts(666); // 获取id为 666 的产品信息
productService.getProducts(); // 获取所有的产品信息 

十二、TypeScript 泛型

1. 泛型语法
function identity <T, U>(value: T, message: U) : T {
  console.log(message);
  return value;
}

console.log(identity<Number, string>(68, "Semlinker"));
function identity <T, U>(value: T, message: U) : T {
  console.log(message);
  return value;
}

console.log(identity(68, "Semlinker"));
2. 泛型接口
interface GenericIdentityFn<T> {
  (arg: T): T;
}
3. 泛型类
class GenericNumber<T> {
  zeroValue: T;
  add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function (x, y) {
  return x + y;
};
4. 泛型工具类型
  1. typeof
interface Person {
  name: string;
  age: number;
}

const sem: Person = { name: 'semlinker', age: 33 };
type Sem= typeof sem; // -> Person

function toArray(x: number): Array<number> {
  return [x];
}

type Func = typeof toArray; // -> (x: number) => number[]
  1. keyof
interface Person {
  name: string;
  age: number;
}

type K1 = keyof Person; // "name" | "age"
type K2 = keyof Person[]; // "length" | "toString" | "pop" | "push" | "concat" | "join" 
type K3 = keyof { [x: string]: Person };  // string | number
interface StringArray {
  // 字符串索引 -> keyof StringArray => string | number
  [index: string]: string; 
}

interface StringArray1 {
  // 数字索引 -> keyof StringArray1 => number
  [index: number]: string;
}
  1. in
type Keys = "a" | "b" | "c"

type Obj =  {
  [p in Keys]: any
} // -> { a: any, b: any, c: any }
  1. infer
type ReturnType<T> = T extends (
  ...args: any[]
) => infer R ? R : any;
  1. extends
interface Lengthwise {
  length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}
loggingIdentity(3);  // Error, number doesn't have a .length property

这时我们需要传入符合约束类型的值,必须包含必须的属性:

loggingIdentity({length: 10, value: 3});
  1. Partial

定义:

/**
 * node_modules/typescript/lib/lib.es5.d.ts
 * Make all properties in T optional
 */
type Partial<T> = {
  [P in keyof T]?: T[P];
};
interface Todo {
  title: string;
  description: string;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
  return { ...todo, ...fieldsToUpdate };
}

const todo1 = {
  title: "Learn TS",
  description: "Learn TypeScript",
};

const todo2 = updateTodo(todo1, {
  description: "Learn TypeScript Enum",
});
{
   title?: string | undefined;
   description?: string | undefined;
}

十三、TypeScript 装饰器

1. 装饰器是什么
2. 装饰器的分类
tsc --target ES5 --experimentalDecorators
复制代码
{
  "compilerOptions": {
     "target": "ES5",
     "experimentalDecorators": true
   }
}
3. 类装饰器
declare type ClassDecorator = <TFunction extends Function>(
  target: TFunction
) => TFunction | void;
function Greeter(target: Function): void {
  target.prototype.greet = function (): void {
    console.log("Hello Semlinker!");
  };
}

@Greeter
class Greeting {
  constructor() {
    // 内部实现
  }
}

let myGreeting = new Greeting();
myGreeting.greet(); // console output: 'Hello Semlinker!';
function Greeter(greeting: string) {
  return function (target: Function) {
    target.prototype.greet = function (): void {
      console.log(greeting);
    };
  };
}

@Greeter("Hello TS!")
class Greeting {
  constructor() {
    // 内部实现
  }
}

let myGreeting = new Greeting();
myGreeting.greet(); // console output: 'Hello TS!';
4. 属性装饰器
declare type PropertyDecorator = (target:Object, 
  propertyKey: string | symbol ) => void;
function logProperty(target: any, key: string) {
  delete target[key];

  const backingField = "_" + key;

  Object.defineProperty(target, backingField, {
    writable: true,
    enumerable: true,
    configurable: true
  });

  // property getter
  const getter = function (this: any) {
    const currVal = this[backingField];
    console.log(`Get: ${key} => ${currVal}`);
    return currVal;
  };

  // property setter
  const setter = function (this: any, newVal: any) {
    console.log(`Set: ${key} => ${newVal}`);
    this[backingField] = newVal;
  };

  // Create new property with getter and setter
  Object.defineProperty(target, key, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true
  });
}

class Person { 
  @logProperty
  public name: string;

  constructor(name : string) { 
    this.name = name;
  }
}

const p1 = new Person("semlinker");
p1.name = "kakuqo";
Set: name => semlinker
Set: name => kakuqo
5. 方法装饰器
declare type MethodDecorator = <T>(target:Object, propertyKey: string | symbol,         
  descriptor: TypePropertyDescript<T>) => TypedPropertyDescriptor<T> | void;
function LogOutput(tarage: Function, key: string, descriptor: any) {
  let originalMethod = descriptor.value;
  let newMethod = function(...args: any[]): any {
    let result: any = originalMethod.apply(this, args);
    if(!this.loggedOutput) {
      this.loggedOutput = new Array<any>();
    }
    this.loggedOutput.push({
      method: key,
      parameters: args,
      output: result,
      timestamp: new Date()
    });
    return result;
  };
  descriptor.value = newMethod;
}

class Calculator {
  @LogOutput
  double (num: number): number {
    return num * 2;
  }
}

let calc = new Calculator();
calc.double(11);
// console ouput: [{method: "double", output: 22, ...}]
console.log(calc.loggedOutput); 
6. 参数装饰器
declare type ParameterDecorator = (target: Object, propertyKey: string | symbol, 
  parameterIndex: number ) => void
function Log(target: Function, key: string, parameterIndex: number) {
  let functionLogged = key || target.prototype.constructor.name;
  console.log(`The parameter in position ${parameterIndex} at ${functionLogged} has
    been decorated`);
}

class Greeter {
  greeting: string;
  constructor(@Log phrase: string) {
    this.greeting = phrase; 
  }
}

// console output: The parameter in position 0 
// at Greeter has been decorated

十四、TypeScript 4.0 新特性

1. 构造函数的类属性推断
class Person {
  fullName; // (property) Person.fullName: string
  firstName; // (property) Person.firstName: string
  lastName; // (property) Person.lastName: string

  constructor(fullName: string) {
    this.fullName = fullName;
    this.firstName = fullName.split(" ")[0];
    this.lastName =   fullName.split(" ")[1];
  }  
}
class Person {
  // Member 'fullName' implicitly has an 'any' type.(7008)
  fullName; // Error
  firstName; // Error
  lastName; // Error

  constructor(fullName: string) {
    this.fullName = fullName;
    this.firstName = fullName.split(" ")[0];
    this.lastName =   fullName.split(" ")[1];
  }  
}
class Person {
   fullName;  // (property) Person.fullName: string
   firstName; // (property) Person.firstName: string | undefined
   lastName; // (property) Person.lastName: string | undefined

   constructor(fullName: string) {
     this.fullName = fullName;
     if(Math.random()){
       this.firstName = fullName.split(" ")[0];
       this.lastName =   fullName.split(" ")[1];
     }
   }  
}
2. 标记的元组元素
function addPerson(...args: [string, number]): void {
  console.log(`Person info: name: ${args[0]}, age: ${args[1]}`)
}

addPerson("lolo", 5); // Person info: name: lolo, age: 5 
function addPerson(name: string, age: number) {
  console.log(`Person info: name: ${name}, age: ${age}`)
}
function addPerson(...args: [name: string, age: number]): void {
  console.log(`Person info: name: ${args[0]}, age: ${args[1]}`);
}
// 未使用标签的智能提示
// addPerson(args_0: string, args_1: number): void
function addPerson(...args: [string, number]): void {
  console.log(`Person info: name: ${args[0]}, age: ${args[1]}`)
} 

// 已使用标签的智能提示
// addPerson(name: string, age: number): void
function addPerson(...args: [name: string, age: number]): void {
  console.log(`Person info: name: ${args[0]}, age: ${args[1]}`);
} 

十五、编译上下文

1. tsconfig.json 的作用
2. tsconfig.json 重要字段
3. compilerOptions 选项
{
  "compilerOptions": {
    /* 基本选项 */
    "target": "es5",
    // 指定 ECMAScript 目标版本: 'ES3' (default), 'ES5', 'ES6'/'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
    // 为装饰器提供元数据的支持
  }
}

十六、TypeScript 开发辅助工具

1. TypeScript Playground
2. TypeScript UML Playground
3. JSON TO TS
4. Schemats
5. TypeScript AST Viewer
6. TypeDoc
7. TypeScript ESLint
上一篇 下一篇

猜你喜欢

热点阅读