【TypeScript 笔记记录】基础类型、接口、函数、泛型

110 阅读7分钟

一、基础类型

  1. 布尔值

    let isDone: boolean = false

  2. 数字

    TypeScript中的所有数字都是浮点数。 支持十进制、十六进制字面量,还有ES 2015中的二进制和八进制字面量。 let count: number = 10

  3. 字符串

    let name: string = 'lyb'

  4. 数组

    写法一: 元素类型[]let list: number[] = [1, 3, 5]

    写法二: 数组类型<元素类型>let list: Array<number> = [1, 3, 5]

  5. 元组Tuple

    元组允许表示一个已知的元素数量和类型的数组,各元素类型不必相同。

    let tuple: [string, number];
    tuple = ['hello', 520] // 正确
    tuple2 = [520, 'hello'] // 错误
    

    当访问一个越界的元素,会使用联合类型替代:(疑问??)

    x[3] = 'world'; // 为什么我的编辑器报错???
    console.log(x[5].toString()); // 为什么我的编辑器报错???
    
  6. 枚举enum

    可以为一组数值赋予友好的名字。

    a. 默认情况下:从0开始编号:

    enum Color {
      Red,
      Green,
      Blue
    }
    
    let c: Color = Color.Green; // 1
    

    b. 手动指定数值的情况下:

      enum Color {
        Red = 1,
        Green = 4,
        Blue
      }
    
      let c: Color = Color.Blue; // 5
    

    枚举类型提供的一个便利是你可以得到由枚举类型的值得到他的名字:

    enum Color {
      Red = 1,
      Green = 4,
      Blue
    }
    let colorName: string = Color[4]; // Green
    
  7. 任意值any

    为那些在编程阶段不确定的变量指定一个类型。

    let list1: any = 4;
    let list2: any = 'lyb';
    let list3: any = [1, 2, 3];
    
    let list4: any[] = [1, true, 'free']
    
  8. 空值void

    在某种程度上,void类型与any类型相反。表示没有任何类型。当一个函数没有返回值时,他的返回值类型是void。

    const warnUser = (): void => {
      alert('this is my warning message~')
    }
    
    let a: void = undefined
    
  9. null和undefined

    typescript中,null和undefined由自己的类型,分别是null和undefined。

    let u: undefined = undefined;
    let n: null = null;
    

    默认情况下:null和undefined是所有类型的子类型,你可以把null和undefined赋值给number类型的变量。

  10. 永不存在的类型never

    never类型是那些总是会抛出异常或根本不会有返回值的函数表达式或箭头函数表达式的返回值类型。 never是所有类型的子类型,可以赋值给任何类型。

    // 返回never的函数必须存在无法达到的终点
    function error(message: string): never {
      throw new Error(message);
    }
    
    // 推断的返回值类型是never
    function fail() {
      return error('something failed');
    }
    
    // 返回never的函数必须存在无法达到的终点
    function infiniteLoop(): never {
      while(true) {}
    }
    
  11. 类型断言

    类型断言好比其他语言中的类型转换,没有运行时的影响,只是在编译阶段起作用

    a. 写法一

    let someValue: any = 'this is a string';
    let svlength: number = (<string>someValue).length;
    

    b. 写法二

    let svlength2: number = (someValue as string).length;
    console.log(svlength2);
    

    总结:两种写法是等价的。在TypeScript中使用JSX时,只有as语法断言是被允许的。

二、接口(interface)

  1. 介绍

    TypeScript的核心原则是对值所具有的结构进行类型检查。 有时被称为“鸭式辨型法”或“结构性子类型化”。 接口的作用就是为这些类型命名和为你的代码或第三方代码定义契约。

  2. 接口初探

    interface LabelledValue {
      label: string;
    }
    
    function printLable(labelledObj: LabelledValue) {
      console.log(labelledObj.label);
    }
    
    let myObj = {
      size: 10,
      label: "Size 10 Ojb"
    }
    
    printLable(myObj);
    

    只要传入的对象满足上面接口中的条件,就是被允许的!需要注意的是类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也对就可以。

  3. 可选属性(?)

    接口里的属性不全都是必需的。

    interface SquareConfig {
      color?: string;
      width?: number;
    }
    
    function createSquare(config: SquareConfig)
    : {color: string; area: number} {
      let newSquare = {color: 'white', area: 100}
      if(config.color) {
        newSquare.color = config.color;
      }
      if(config.width) {
        newSquare.area = config.width * config.width;
      }
    
      return newSquare;
    }
    
    let mySquare = createSquare({color: 'black'});
    console.log(mySquare); // {"color": "black","area": 100}
    

    在可选属性名后加一个?符号

    可选属性的好处:

    • 可以对可能存在的属性进行预定义
    • 可以捕获引用了不存在的属性时的错误
  4. 只读属性(readonly)

    一些对象属性只能在创建的时候修改其值。在属性名前用readonly指定只读属性。

    interface Point {
      readonly x: number;
      readonly y: number;
    }
    
    let p1: Point = { x: 10, y: 20 };
    p1.x = 5; // error
    
    • TypeScript具有ReadonlyArray<T>类型,确保创建后的数组不能被修改:

      let a: number[] = [1, 23, 4]
      let ro: ReadonlyArray<number> = a;
      ro[1] = 12; // error
      ro.push(5); // error
      ro.length =  100; // error
      a = ro; // error
      
    • a=ro可以看到就算把整个ReadonlyArray赋值到一个普通数组也是不可以的,但是可以用类型断言重写: a = ro as number[]

    注意:readonly VS const 做为变量的话用const,做为属性则用readonly

  5. 额外的属性检查

    看如下代码,调用createSquare函数的时候,传入的参数是colour,而不是color,那么会报错。

    interface SquareConfig {
      color?: string;
      width?: number;
    }
    
    function createSquare(config: SquareConfig)
    : {color: string; area: number} {
      let newSquare = {color: 'white', area: 100}
      if(config.color) {
        newSquare.color = config.color;
      }
      if(config.width) {
        newSquare.area = config.width * config.width;
      }
    
      return newSquare;
    }
    
    let mySquare = createSquare({colour: 'black'}) ; // 报错
    

    因为对象字面量会被特殊对待而会经过额外属性检查。 解决该问题的方法有三个:

    • 类型断言 let mySquare = createSquare({colour: 'black'} as SquareConfig) ;
    • 索引签名
      interface SquareConfig {
        color?: string;
        width?: number;
        [propName: string]: any;
      }
      
    • 将对象赋值给另一个变量
      // 注意:对象里的属性至少有一个接口中的配置项 比如:删除width,就会报错
      let squareOptions = {colour: 'black', width: 100};
      let mySquare = createSquare(squareOptions);
      
  6. 函数类型

    接口除了可以描述普通对象外,还能描述函数类型。

    // 1.定义函数类型接口
    interface SearchFn {
      (sourse: string, subString: string): boolean;
    }
    
    // 2.使用
    let mySearch: SearchFn;
    
    mySearch = function(sourse: string, subString: string) {
      let res = sourse.search(subString)
      return res > -1
    }
    
    mySearch = function(src: string, sub: string): boolean {
      let res = src.search(sub);
      return res > -1;
    }
    
    // 类型推断
    mySearch = function(src, sub){
      let res = src.search(sub);
      return res > -1;
    }
    
  7. 可索引的类型(没太懂)

    • 字符串索引
    • 数字索引

三、函数(function)

TS里的函数也可以创建有名字的函数和匿名函数

  1. 函数类型 函数的注解方式:

    • 函数声明方式:
    function fn(a: number, b: number): number { return a + b }
    
    • 函数表达式注解方式:
    // 采用了推断类型写法(左侧指定了类型,右侧没有类型)
    let fn: (a: number, b: number) => {} 
        = function(a, b) { return a: 1 }
    
  2. 可选参数和默认参数 参数后加?符号,代表可选参数

    function buildName (firstName: string, lastName?: string) {
      return firstName + " " + lastName;
    }
    
    

    参数直接提供默认值=,代表默认参数

    function buildName2 (firstName: string, lastName = 'smith') {
      return firstName + " " + lastName;
    }
    
  3. 剩余参数

    你想同时操作多个参数,或者你不知道会有多少参数传递进来,那么你可以使用arguments来访问所有传入的参数。

    使用...符号,代表剩余参数

    function buildName3 (firstName: string, ...restOfName: string[]) {
      return firstName + " " + restOfName.join(" ");
    }
    
  4. 函数的重载

    意义:表意更清楚

    function reverse(x: number): number;
    function reverse(x: string): string;
    function reverse(x: string | number) {
      if(typeof x === 'string') {
        return x.split('').reverse().join('')
      }
    
      if(typeof x === 'number') {
        return Number(x.toString().split('').reverse().join(''))
      }
    }
    

四、泛型(generic)

  1. 介绍

    软件工程中,我们不仅需要创建一致的API,同时还要考虑可重用性。 可以使用泛型来创建可以重用的组件,可以支持多种类型的数据。

  2. 初识泛型 定义泛型:

    function identity<T>(arg: T): T {
      return arg;
    }
    

    T: 类型变量 这样我们可以知道参数类型与返回值类型是相同的

    使用泛型:

    • 方法一: let output = identity<string>("mystring")
    • 方法二: let output = identity("mystring")
  3. 使用泛型变量

    // 写法一:
    function loggingIdentity<T>(arg: T[]): T[] {
      return arg;
    }
    
    // 写法二:
    function loggingIdentity1<T>(arg: Array<T>): Array<T> {
      return arg;
    }
    
  4. 泛型类型

    • 泛型函数:
    function identity<T>(arg: T): T {
      return arg;
    }
    
    // 可以使用不同的泛型参数名,只要数量上和使用形式上能对应就好
    let myIdentity: <U>(arg: U): U => U = identity;
    
    
    • 使用带有调用签名的对象字面量来定义泛型函数:
    function identity<T>(arg: T): T {
      return arg;
    }
    
    let myIdentity: {<U>(arg: U): U} = identity;
    
    • 引入泛型接口:
    interface GenericIdentityFn {
      <T>(arg: T): T;
    }
    
    function identity<T>(arg: T): T {
      return arg;
    }
    
    let myIdentity: GenericIdentityFn = identity;
    let myIdentity2: {<U>(arg: U): U} = identity;
    
    • 最终泛型写法:
    interface GenericIdentityFn<T> {
      (arg: T): T;
    }
    
    function identity<T>(arg: T): T {
      return arg;
    }
    
    let myIdentity: GenericIdentityFn<number> = identity;
    
  5. 泛型约束(extends)

    如果没有类型约束,那么loggingIdentity该函数的参数arg就不能使用他没有的属性length

    interface Lengthwise {
      length: nubmer;
    }
    
    function loggingIdentity<T extends Lengthwise>(arg: T): T {
      console.log(arg.length)
    
      return arg;
    }