理论学习

类型推断

类型推断分为 基础类型推断、联合类型推断、上下文类型推断

  1. // 基础类型推断
  2. let a = 1; //推断为number
  3. let add = (value = 1) => value + 1; // 推断参数和返回值是number
  4. // 联合类型推断
  5. let b = [1, 'abc']; //推断为number和string的联合类型
  6. // 上下文类型推断
  7. window.onkeydown = (event) => {
  8. console.log(event.button); // 推断为键盘事件,因为没有button,所以会报错
  9. }


类型断言,当你对自己的代码比TS对类型推断更自信时,可以明确指定类型,但注意不能滥用。下面是几个例子:

  1. interface Foo {
  2. id: number
  3. }
  4. //报错
  5. let foo = {}
  6. foo.id = 1001;//Property 'id' does not exist on type '{}'.
  7. console.log(foo.id);//Property 'id' does not exist on type '{}'.
  8. console.log((foo as Foo).id);//right
  9. //正常
  10. let foo: Foo = {
  11. id: 1001
  12. }
  13. console.log(foo.id);
  1. interface Note {
  2. id?: number
  3. }
  4. function deleteNote(id:number) {
  5. console.log(id);
  6. }
  7. let note:Note = {id: 123}
  8. //报错
  9. deleteNote(note.id); //Argument of type 'number | undefined' is not assignable to parameter of type 'number'.
  10. Type 'undefined' is not assignable to type 'number'.
  11. deleteNote(note.id as number); // 类型断言为number

类型兼容

参考
image.png

  1. // 接口兼容性
  2. interface X {
  3. a: any;
  4. b: any;
  5. }
  6. interface Y {
  7. a: any;
  8. b: any;
  9. c: any;
  10. }
  11. let x: X = {a: 1, b: 2};
  12. let y: Y = {a: 1, b: 2, c: 3};
  13. x = y;
  14. y = x; // Property 'c' is missing in type 'X' but required in type 'Y'.
  15. // 函数兼容性
  16. type Handler = (a: number, b: number) => void;
  17. function hof(handler: Handler) {
  18. return handler;
  19. }
  20. // 1) 参数个数
  21. let handler1 = (a: number) => {}
  22. hof(handler1);
  23. let handler2 = (a: number, b: number, c: number) => {};
  24. hof(handler2); // Argument of type '(a: number, b: number, c: number) => void' is not assignable to parameter of type 'Handler'.ts(2345)
  25. // 可选参数和剩余参数
  26. let a = (p1: number, p2: number) => {}
  27. let b = (p1?: number, p2?: number) => {}
  28. let c = (...args: number[]) => {}
  29. a = b;
  30. a = c;
  31. b = c; // error
  32. b = a; // error
  33. c = a; // error
  34. c = b; // error
  35. // 2)参数类型
  36. let handler3 = (a: string) => {}
  37. hof(handler3); //Argument of type '(a: string) => void' is not assignable to parameter of type 'Handler'.
  38. interface Point3D {
  39. x: number;
  40. y: number;
  41. z: number;
  42. }
  43. interface Point2D {
  44. x: number;
  45. y: number;
  46. }
  47. let p3d = (point: Point3D) => {};
  48. let p2d = (point: Point2D) => {}
  49. p3d = p2d;
  50. p2d = p3d; // Type '(point: Point3D) => void' is not assignable to type '(point: Point2D) => void'.
  51. // 3)返回值类型
  52. let f = () => ({name: 'Alice'})
  53. let g = () => ({name: 'Alice', location: 'shanghai'});
  54. f = g;
  55. g = f; // Type '() => { name: string; }' is not assignable to type '() => { name: string; location: string; }'.
  56. function overload(a: number): number;
  57. function overload(a: string): string;
  58. function overload(a: any): any {
  59. // 前两个是声明,第三个是实现,并且应该是更宽的类型,否则会报错
  60. }
  61. // 枚举兼容性
  62. enum Fruit { Apple }
  63. let fruit: Fruit.Apple = 3;
  64. let no: number = Fruit.Apple;
  65. enum Color { Red }
  66. let color: Color.Red = Fruit.Apple; // Type 'Fruit' is not assignable to type 'Color'.
  67. // 类的兼容性
  68. class A {
  69. constructor(p: number, q: number) {}
  70. id = 1;
  71. // private name = ''; // 增加此行(私有属性)后 a = b 报错
  72. }
  73. class B {
  74. static s = 1;
  75. constructor(p: number) {}
  76. id = 2;
  77. }
  78. let a = new A(1, 2);
  79. let b= new B(2);
  80. a = b; // 可以比较,因为静态成员和构造函数不参与比较,实例成员id做比较
  81. b = a; // 同上
  82. class C extends A {
  83. }
  84. let cc = new C(1, 2);
  85. aa = cc; // 继承的类可以兼容
  86. cc = aa; // 同上
  87. // 泛型兼容性
  88. interface Empty<T> {
  89. value: T; // 如果不增加T的具体使用,那么是可以相互兼容的
  90. }
  91. let obj1: Empty<number> = { value: 1 };
  92. let obj2: Empty<string> = { value: '1'};
  93. obj1 = obj2; // Type 'Empty<string>' is not assignable to type 'Empty<number>'.
  94. let log1 = <T>(x: T): T => {
  95. return x
  96. }
  97. let log2 = <U>(y: U): U => {
  98. return y
  99. }
  100. log1 = log2

类型保护

lang因为是联合类型(JavaScript|Java),在分支判定中,各种使用了类型断言,代码比较冗余

  1. enum Type {
  2. Strong,
  3. Weak
  4. }
  5. class Java {
  6. java: any;
  7. helloJava() {
  8. console.log('hello java');
  9. }
  10. }
  11. class JavaScript {
  12. javascript: any;
  13. helloJavaScript() {
  14. console.log('hello javascript');
  15. }
  16. }
  17. function isJava(lang: Java | JavaScript): lang is Java {
  18. return (lang as Java).helloJava !== undefined;
  19. }
  20. function getLanguage(type: Type, x: string | number) {
  21. let lang = (type === Type.Weak) ? new JavaScript() : new Java();
  22. if ((lang as JavaScript).helloJavaScript) {
  23. (lang as JavaScript).helloJavaScript();
  24. } else {
  25. (lang as Java).helloJava();
  26. }
  27. return lang;
  28. }
  29. getLanguage(Type.Strong);


为了让TS能识别类型,有几种实现方法

  1. instanceof
  2. in ( 这里特意引入了两个属性,分别是两个类特有)
  3. typeof 这里引入了第二个参数(抛开lang来讲解类型识别)
  4. isJava引入了一个判定方法,注意语法 ```json function isJava(lang: Java | JavaScript): lang is Java { return (lang as Java).helloJava !== undefined; }

function getLanguage(type: Type, x: string | number) { // instanceof if (lang instanceof Java) { lang.helloJava(); } else { lang.helloJavaScript(); }

//in if (‘java’ in lang) { lang.helloJava(); } else { lang.helloJavaScript(); }

// typeof if (typeof x === ‘string’) { x.length } else { x.toFixed(2); }

// 类型判断方法 if (isJava(lang)) { lang.helloJava() } else { lang.helloJavaScript(); }

return lang; }

  1. <a name="j4ajV"></a>
  2. ## 直接传递函数导致上下文丢失
  3. ```javascript
  4. // 错误的写法
  5. const [input, setInput] = useState('');
  6. const handleInputChange = (e) => { //Parameter 'e' implicitly has an 'any' type.
  7. setInput(e.target.value);
  8. }
  9. <textarea
  10. ref={textRef}
  11. value={input}
  12. placeholder="请输入内容…"
  13. onChange={handleInputChange} />
  1. // 正确的写法1
  2. <textarea
  3. ref={textRef}
  4. value={input}
  5. placeholder="请输入内容…"
  6. onChange={(e) => setInput(e.target.value)} />
  7. // 正确的写法2
  8. const [input, setInput] = useState('');
  9. const handleInputChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
  10. setInput(e.target.value);
  11. }
  12. <textarea
  13. ref={textRef}
  14. value={input}
  15. placeholder="请输入内容…"
  16. onChange={handleInputChange} />

useState未正确指明类型导致的报错

错误的写法

  1. const [ noteList, setNoteList ] = useState<>([]);
  2. setNoteList((prevList) => [ note, ...prevList ])

Argument of type ‘(prevList: never[]) => Note[]’ is not assignable to parameter of type ‘SetStateAction‘.

正确的写法(指定了类型)

  1. const [ noteList, setNoteList ] = useState<Note[]>([]);
  2. setNoteList((prevList) => [ note, ...prevList ])

IndexedDB进行插入操作报错

对于primary key,如果id为undefined是会报错的,需要删除无用id值

useRef创建的HTMLTextAreaElement对象报错

问题描述

  1. const textRef = useRef<HTMLTextAreaElement>()
  2. <textarea ref={textRef} placeholder="请输入内容…"/>

Type ‘MutableRefObject‘ is not assignable to type ‘RefObject‘.

解决办法

  1. const textRef = useRef<HTMLTextAreaElement>(null)

自己只有一些推断,还无法给到解释。