# 类型别名type与字面量类型

# 类型别名

类型别名就是给一种类型起个别的名字,之后只要使用这个类型的地方,都可以用这个名字作为类型代替,但是它只是起了一个名字,并不是创建了一个新类型。

这种感觉就像 JS 中对象的赋值,你可以把一个对象赋给一个变量,使用这个对象的地方都可以用这个变量代替,但你并不是创建了一个新对象,而是通过引用来使用这个对象。

# 基本用法

怎么定义类型别名,使用 type 关键字:

type TypeString = string;
let str: TypeString;
str = 123; // error Type '123' is not assignable to type 'string'

类型别名也可以使用泛型,来看例子:

type PositionType<T> = { x: T; y: T };
const position1: PositionType<number> = {
  x: 1,
  y: -1
};
const position2: PositionType<string> = {
  x: "right",
  y: "top"
};

使用类型别名时也可以在属性中引用自己:

type Child<T> = {
  current: T;
  child?: Child<T>;
};
let ccc: Child<string> = {
  current: "first",
  child: {
    // error
    current: "second",
    child: {
      current: "third",
      child: "test" // 这个地方不符合type,造成最外层child处报错
    }
  }
};

# 注意的点

  1. 类型别名只可以在对象属性中引用类型别名自己,不能直接使用,比如下面这样是不对的:
type Child = Child[]; // error 类型别名“Child”循环引用自身
  1. 因为类型别名只是为其它类型起了个新名字来引用这个类型,所以当它为接口起别名时,不能使用 extendsimplements

接口和类型别名有时可以起到同样作用,比如下面这个例子:

type Alias = {
  num: number;
};
interface Interface {
  num: number;
}
let _alias: Alias = {
  num: 123
};
let _interface: Interface = {
  num: 321
};
_alias = _interface;

可以看到用类型别名和接口都可以定义一个只包含 num 属性的对象类型,而且类型是兼容的。

那么什么时候用类型别名,什么时候用接口呢?

可以通过两点来选择:

  • 当你定义的类型要用于拓展,即使用 implements 等修饰符时,用接口;
  • 当无法通过接口,并且需要使用联合类型或元组类型,用类型别名。

# 字面量类型

字面量类型其实比较基础,但是它又不适合放到基本类型里讲,因为字符串字面量类型和字符串类型其实并不一样,所以接下来我们来学习两种字面量类型。

# 字符串字面量类型

字符串字面量类型其实就是字符串常量,与字符串类型不同的是它是具体的值。

type Name = "Lison";
const name1: Name = "test"; // error 不能将类型“"test"”分配给类型“"Lison"”
const name2: Name = "Lison";

你还可以使用联合类型来使用多个字符串:

type Direction = "north" | "east" | "south" | "west";
function getDirectionFirstLetter(direction: Direction) {
  return direction.substr(0, 1);
}
getDirectionFirstLetter("test"); // error 类型“"test"”的参数不能赋给类型“Direction”的参数
getDirectionFirstLetter("east");

# 数字字面量类型

另一个字面量类型就是数字字面量类型,它和字符串字面量类型差不多,都是指定类型为具体的值。

type Age = 18;
interface Info {
  name: string;
  age: Age;
}
const info: Info = {
  name: "Lison",
  age: 28 // error 不能将类型“28”分配给类型“18”
};

这里补充一个比较经典的逻辑错误,来看例子:

function getValue(index: number) {
  if (index !== 0 || index !== 1) {
    // error This condition will always return 'true' since the types '0' and '1' have no overlap
    // ...
  }
}

这个例子中,在判断逻辑处使用了 || 符,当 index !== 0 不成立时,说明 index 就是 0,则不应该再判断 index 是否不等于 1;而如果 index !== 0 成立,那后面的判断也不会再执行;所以这个地方会报错。

# interface与type(重点)

interface  和  type  相类似,但不完全一致,type 可以校验基础类型,而 Interface 不支持基础类型的校验。

TS 里,能使用 Interface 的话就使用 Interface

# 共同点

interface 和 type 都可以拓展,并且两者并不是相互独立的,也就是说 interface 可以 extends type, type 也可以 extends interface 。 虽然效果差不多,但是两者语法不同

  1. interface extends interface

    interface Name { 
      name: string; 
    }
    interface User extends Name { 
      age: number; 
    }
    
  2. type extends type

    type Name = { 
      name: string; 
    }
    type User = Name & { age: number  };
    
  3. interface extends type

    type Name = { 
      name: string; 
    }
    interface User extends Name { 
      age: number; 
    }
    
  4. type extends interface

    interface Name { 
      name: string; 
    }
    type User = Name & { 
      age: number; 
    }
    

# 不同点

  1. type 可以而 interface 不行
  • type 可以声明基本类型别名,联合类型,元组等类型

    // 基本类型别名
    type Name = string
    
    // 联合类型
    interface Dog {
        wong();
    }
    interface Cat {
        miao();
    }
    
    type Pet = Dog | Cat
    
    // 具体定义数组每个位置的类型
    type PetList = [Dog, Pet]
    
  • type 语句中还可以使用 typeof 获取实例的 类型进行赋值

    // 当你想获取一个变量的类型时,使用 typeof
    let div = document.createElement('div');
    type B = typeof div
    
  • 其他骚操作

    type StringOrNumber = string | number;  
    type Text = string | { text: string };  
    type NameLookup = Dictionary<string, Person>;  
    type Callback<T> = (data: T) => void;  
    type Pair<T> = [T, T];  
    type Coordinates = Pair<number>;  
    type Tree<T> = T | { left: Tree<T>, right: Tree<T> };
    
  1. interface 可以而 type 不行

    interface 能够声明合并

    interface User {
      name: string
      age: number
    }
    
    interface User {
      sex: string
    }
    
    /*
    User 接口为 {
      name: string
      age: number
      sex: string 
    }
    */