基本写法对照表

Typescript Javascript(ES6) 说明
let isDone:boolean = true let isDone = true 定义布尔类型
let str:string = ‘abc’ let str = ‘abc’ 定义字符串类型
let num:number = 10 let num = 10 定义数字类型
let arr:number[] = [1, 2, 3]
let arrStr:string[] = [‘a’, ‘b’, ‘c’]
let arrX:Array = [‘a’, ‘b’, ‘c’]
let arr = [1, 2, 3]
let arrStr = [‘a’, ‘b’, ‘c’]
let arrX = [‘a’, ‘b’, ‘c’]
定义数组类型
let peopleInfo:[string, number] = [‘male’, 23] 定义元组类型(Tuple)
enum Direction { Up = 1, Down, Left, Right } 定义枚举类型(enum)
let s:any = 4 定义任意类型
let unusable:void = undefined
let u:undefined = undefined
let n:null = null
let u = undefined
let n = null
never never类型表示的是那些永不存在的值的类型

关于枚举类型的补充

  1. enum Direction { Up, Down, Left, Right }
  2. let up = Direction.Up
  3. let down = Direction.Down
  4. let left = Direction.Left
  5. let right = Direction.Right
  6. console.log(up, down, left, right)
  7. // -> 0, 1, 2, 3
  8. enum Direction { Up = 1, Down, Left, Right }
  9. let up = Direction.Up
  10. let down = Direction.Down
  11. let left = Direction.Left
  12. let right = Direction.Right
  13. console.log(up, down, left, right)
  14. // -> 1, 2, 3, 4
  15. enum Direction { Up = 8, Down = 10, Left = 12, Right = 14 }
  16. let up = Direction.Up
  17. let down = Direction.Down
  18. let left = Direction.Left
  19. let right = Direction.Right
  20. console.log(up, down, left, right)
  21. // -> 8, 10, 12, 14

关于void和never的补充

  1. // 没有返回值 返回个空
  2. function warnUser():void {
  3. console.log("This is my warning message")
  4. }
  5. // 没有返回值,永远到不了
  6. function error(message: string):never {
  7. throw new Error(message)
  8. }

关于函数的基本用法

  1. interface Person {
  2. name: string,
  3. age?: number // 可选属性
  4. }
  5. function getPersonName (person: Person): string {
  6. return person.name
  7. }
  8. const pName = getPersonName({ name: 'zhangsan', age: 20 })
  9. console.log(pName)
  10. const qName = getPersonName({ name: 'lisi' })
  11. console.log(qName)
  12. // 可选形参,无返回,且有以下三种写法
  13. function add1 (x: number, y?: number): void {
  14. console.log(x, y)
  15. }
  16. const add2 = function (x: number, y?: number): void {
  17. console.log(x, y)
  18. }
  19. const add3 = (x: number, y?: number): void => {
  20. console.log(x, y)
  21. }

总而言之,就是要明确的知道你传入的是什么,返回的是什么。不是心里知道,而是要通过代码体现出来。一般JS的规范就是要求你心里知道,记不住的写注释,TS从代码上体现出来,同时配合vscode这样的IDE和eslint语法检查,确实能很清楚知道输入的是什么,返回的是什么。

程序是写给人读的,只是偶尔让计算机执行一下。— Donald Knuth

关于类的基本用法

  1. interface AnimalInfo {
  2. name: string,
  3. age: number,
  4. gender: number,
  5. color?: string
  6. }
  7. // 父类
  8. class Animal {
  9. // 静态方法
  10. static getTen (): number {
  11. return 10
  12. }
  13. // 属性
  14. // 公开属性
  15. public name: string
  16. // 受保护权限
  17. protected gender: number
  18. // 私有属性
  19. private age: number
  20. // 构造函数
  21. constructor (aniInfo: AnimalInfo) {
  22. this.name = aniInfo.name
  23. this.age = aniInfo.age
  24. this.gender = aniInfo.gender
  25. }
  26. // 方法
  27. getName (): string {
  28. return this.name
  29. }
  30. getAge (): number {
  31. return this.age
  32. }
  33. }
  34. // 继承了父类的方法和属性
  35. class Dog extends Animal {
  36. color: string
  37. constructor (animaInfo: AnimalInfo) {
  38. super(animaInfo)
  39. this.color = animaInfo.color || ''
  40. }
  41. getColor (): string {
  42. return `${this.name}的颜色是: ${this.color}`
  43. }
  44. getGender (): number {
  45. return this.gender
  46. }
  47. }
  48. const jinMao = new Dog({
  49. name: '金毛',
  50. age: 2,
  51. gender: 1,
  52. color: '黄色'
  53. })
  54. // 调用父类方法
  55. console.log(jinMao.getName()) // -> 金毛
  56. // 显示属性
  57. console.log(jinMao.getColor()) // -> 金毛的颜色是: 黄色
  58. // 调用静态方法
  59. console.log(Animal.getTen()) // -> 10
  60. // 调用受保护的属性
  61. console.log(jinMao.getGender()) // -> 1
  62. // 调用私有属性
  63. console.log(jinMao.getAge()) // -> 2
  64. // 抽象类
  65. abstract class Book {
  66. name: string
  67. constructor (name: string) {
  68. this.name = name
  69. }
  70. getBookName (): void {
  71. console.log('Book name: ' + this.name)
  72. }
  73. abstract printBook(): void // 必须在派生类中实现
  74. }
  75. class JSBook extends Book {
  76. printBook (): void{
  77. console.log('This book is printing.')
  78. }
  79. }
  80. let book = new JSBook('Javascript')
  81. book.getBookName()
  82. book.printBook()

Interface的说明

  1. interface Person {
  2. name: string,
  3. age?: number, // 可选属性
  4. readonly code: string // 只读 定义变量使用const,而属性使用readonly
  5. }
  6. const createPerson = (info: Person): string => {
  7. console.log(info.name)
  8. return 'creaeted'
  9. }
  10. const result = createPerson({
  11. name: 'zhangsan',
  12. age: 10,
  13. code: '430x'
  14. })
  15. console.log(result)
  16. // 类与接口
  17. // 门是一个类,防盗门是门的子类。如果防盗门有一个报警器的功能,我们可以简单的给防盗门添加一个报警方法。这时候如果有另一个类,车,也有报警器的功能,就可以考虑把报警器提取出来,作为一个接口,防盗门和车都去实现它:
  18. interface Alarm {
  19. alert: () => void;
  20. }
  21. class Door {
  22. }
  23. class SecurityDoor extends Door implements Alarm {
  24. alert () {
  25. console.log('SecurityDoor alert');
  26. }
  27. }
  28. class Car implements Alarm { // 实现多个接口 class Car implements Alarm, Light {
  29. alert () {
  30. console.log('Car alert');
  31. }
  32. }

声明文件

声明文件必需以 .d.ts 为后缀。其实这个文件对于业务没有什么用,但是对TS有用。举个例子,项目中使用了第三方库,而这个第三方库没有ts版本。ts在编译过程中是不知道这个库是什么东西,所以需要显示的告诉ts,我知道我用的是什么,也知道自己在做什么。

现在大部分库都有自己的声明文件,在安装项目依赖,如 @types/koa ,就是在安装koa的声明文件。

大约是这么理解,有出入以后再补充。

理论上用.d.ts文件,是先需要有代码实现,然后才有声明文件,而且声明文件是为没有ts实现的js库而准备的。也可以说是为了欺骗编译器准备。告诉编译器我知道我在做什么,实际上只有天知道。

泛型

在定义接口、函数、类等的时候,无法确定其参数类型,等到调用的时候才能确定,这种场景就需要用到泛型。

场景1:传递进入什么类型返回什么类型

  1. function returnAny<T> (arg: T): T {
  2. return arg;
  3. }
  4. const a = returnAny<number>(1);
  5. const b = returnAny<string>('aa');
  6. console.log(a, b); // -> 1, aa

场景2:传入一个数组,将其中互换位置

  1. function swap<T, U> (tup: [T, U]): [U, T] {
  2. return [tup[1], tup[0]];
  3. }
  4. const c = swap(['a', 1]);
  5. console.log(c); // -> [1, 'a']

场景3:动态输出object的value

  1. const d = {
  2. method: '123',
  3. path: 'ss',
  4. action: (method: string, path: string): void => {
  5. console.log(method, path)
  6. }
  7. };
  8. Object.keys(d).forEach(key => {
  9. console.log(d[key])
  10. })

上面这段写法移植到TS会报错:

  1. 元素隐式具有 “any” 类型,因为类型为 “string” 的表达式不能用于索引类型 “{ method: string; path: string; action: (method: string, path: string) => void; }”。
  2. 在类型 “{ method: string; path: string; action: (method: string, path: string) => void; }” 上找不到具有类型为 “string” 的参数的索引签名。

用泛型可以解决,与场景一有点类似,但是更具体一些

  1. const getProperty = <T, K extends keyof T>(o: T, name: K): T[K] => {
  2. return o[name];
  3. }
  4. Object.keys(d).forEach(key => {
  5. console.log(getProperty(d, key))
  6. })

泛型约束

  1. interface withLength {
  2. length: number
  3. }
  4. function Logger<T extends withLength> (arg:T): T {
  5. console.log(arg.length);
  6. return arg;
  7. }
  8. Logger('abc');
  9. Logger(['a', 'b']);
  10. Logger({length: 12});
  11. // Logger(23); // 没有length不能传递进入

装饰器

使用装饰器需要 tsconfig.json 配置 experimentalDecorators 为true。
装饰器是对类、函数、属性之类的一种装饰,可以针对其添加一些额外的行为。通俗的理解可以认为就是在原有代码外层包装了一层处理逻辑。即使拿掉了也不影响原有的功能。

应用场景举例:

  1. 打logger
  2. 验证数据的正确性

简单的测试使用:

  1. function log (target: any, name: string, descriptor: any) {
  2. console.log(target, name, descriptor)
  3. const _value = descriptor.value;
  4. // 改写方法 这里基本就可以做任何你想做的事情
  5. descriptor.value = function (x: number, y: number): number {
  6. console.log(`算式:${x} + ${y} = ${_value.apply(this, arguments)}`);
  7. return _value.apply(this, arguments)
  8. }
  9. return descriptor
  10. }
  11. class Foo {
  12. @log
  13. add (x: number, y: number): number {
  14. return x + y
  15. }
  16. }
  17. let x = new Foo()
  18. let y = x.add(1, 2)
  19. console.log(y) // -> 算式:1 + 2 = 3 -> 3

上面的例子有个问题,装饰器没有办法传参。可以使用装饰器工厂。

  1. function configurable (value: boolean) {
  2. console.log(1, value)
  3. return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  4. descriptor.configurable = value;
  5. console.log(2, value)
  6. };
  7. }
  8. class Foo {
  9. @configurable(true)
  10. add (x: number, y: number): number {
  11. return x + y
  12. }
  13. }

reflect-metadata

Reflect Metadata 是 ES7 的一个提案,它主要用来在声明的时候添加和读取元数据。关联 tsconfig.json 里配置 emitDecoratorMetadata 选项为 true 。安装npm i reflect-metadata --save

要弄清这个需要弄清ES6的 ReflectProxy