TypeScript基础

84 阅读5分钟

基础类型

TypeScript 支持与 JavaScript 几乎相同的数据类型,此外还提供了实用的枚举类型方便我们使用。

  • 布尔值

    let isDone: boolean = false
    
  • 数字

    let count: number = 10
    
  • 字符串

    let name: string = "nick"
    
  • 数组

    let list: number[] = [1, 2, 3];
    
    let list: Array<number> = [1, 2, 3]; // Array<number>泛型语法
    
  • 元组

    元组类型允许表示一个已知元素数量和类型的数组,各元素的类型不必相同。 比如,你可以定义一对值分别为 stringnumber 类型的元组。

    let x: [string, number]
    x = ['hello', 10] // OK
    x = [10, 'hello'] // Error
    
  • 枚举

    使用枚举我们可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。 TypeScript 支持数字的和基于字符串的枚举。

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

    枚举类型提供的一个便利是你可以由枚举的值得到它的名字。 例如,我们知道数值为 2,但是不确定它映射到 Color 里的哪个名字,我们可以查找相应的名字:

    enum Color {Red = 1, Green, Blue}
    let colorName: string = Color[2]
    
    console.log(colorName)  // 显示'Green'因为上面代码里它的值是2
    

    可以看到上述编译过后的js代码

    var Color;
    (function (Color) {
        Color[Color["Red"] = 1] = "Red";
        Color[Color["Green"] = 2] = "Green";
        Color[Color["Blue"] = 3] = "Blue";
    })(Color || (Color = {}));
    var colorName = Color[2];
    console.log(colorName); // 显示'Green'因为上面代码里它的值是2
    
  • any

    有时候,我们会想要为那些在编程阶段还不清楚类型的变量指定一个类型。 这些值可能来自于动态的内容,比如来自用户输入或第三方代码库。 这种情况下,我们不希望类型检查器对这些值进行检查而是直接让它们通过编译阶段的检查。

    在 TypeScript 中,任何类型都可以被归为 any 类型。这让 any 类型成为了类型系统的顶级类型(也被称作全局超级类型)。

    let notSure: any = 4
    notSure = 'maybe a string instead'
    notSure = false // 也可以是个 boolean
    
  • void

    某种程度上来说,void 类型像是与 any 类型相反,它表示没有任何类型。 当一个函数没有返回值时,你通常会见到其返回值类型是 void

    function warnUser(): void {
      console.log('This is my warning message')
    }
    
  • null和undefined

    TypeScript 里,undefinednull 两者各自有自己的类型分别叫做 undefinednull。 和 void 相似,它们的本身的类型用处不是很大:

    let u: undefined = undefined
    let n: null = null
    
  • never

    never 类型表示的是那些永不存在的值的类型。 例如, never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never 类型,当它们被永不为真的类型保护所约束时。

    never 类型是任何类型的子类型,也可以赋值给任何类型;然而,没有类型是 never 的子类型或可以赋值给never 类型(除了 never 本身之外)。 即使 any 也不可以赋值给 never

    // 返回never的函数必须存在无法达到的终点
    function error(message: string): never {
      throw new Error(message)
    }
    
    // 推断的返回值类型为never
    function fail() {
      return error("Something failed")
    }
    
    // 返回never的函数必须存在无法达到的终点
    function infiniteLoop(): never {
      while (true) {
     }
    
  • object

    object 表示非原始类型,也就是除 numberstringbooleansymbolnullundefined 之外的类型。

    declare function create(o: object | null): void
    
    create({ prop: 0 }) // OK
    create(null) // OK
    
    create(42) // Error
    create('string') // Error
    create(false) // Error
    create(undefined) // Error
    

断言

有时候你会遇到这样的情况,你会比 TypeScript 更了解某个值的详细信息。 通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。

通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。 类型断言好比其它语言里的类型转换,但是不进行特殊的数据检查和解构。 它没有运行时的影响,只是在编译阶段起作用。 TypeScript 会假设你,程序员,已经进行了必须的检查。

//尖括号语法
let someValue: any = 'this is a string'

let strLength: number = (<string>someValue).length

//as语法
let someValue: any = 'this is a string'

let strLength: number = (someValue as string).length

接口

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

interface LabelledValue {
  label: string
}

function printLabel(labelledObj: LabelledValue) {
  console.log(labelledObj.label)
}

let myObj = {size: 10, label: 'Size 10 Object'}
printLabel(myObj)

运行 tsc interface.js 可以看待编译后的js代码:

function printLabel(labelledObj) {
    console.log(labelledObj.label);
}
var myObj = { size: 10, label1: 'Size 10 Object' };
printLabel(myObj);

LabelledValue 接口就好比一个名字,用来描述上面例子里的结构。 它代表了有一个 label 属性且类型为string 的对象。 需要注意的是,我们在这里并不能像在其它语言里一样,说传给 printLabel 的对象实现了这个接口。我们只会去关注值的外形。 只要传入的对象满足上面提到的必要条件,那么它就是被允许的。

还有一点值得提的是,类型检查器不会去检查属性的顺序,只要相应的属性存在并且类型也是对的就可以。