关于javascript:TypeScript-之常见类型上

33次阅读

共计 6102 个字符,预计需要花费 16 分钟才能阅读完成。

TypeScript 的官网文档早已更新,但我能找到的中文文档都还停留在比拟老的版本。所以对其中新增以及订正较多的一些章节进行了翻译整顿。

本篇翻译整顿自 TypeScript Handbook 中「Everyday Types」章节。

本文并不严格依照原文翻译,对局部内容也做了解释补充。

常见类型(Everyday Types)

本章咱们会解说 JavaScript 中最常见的一些类型,以及对应的形容形式。留神本章内容并不详尽,后续的章节会解说更多命名和应用类型的形式。

类型能够呈现在很多中央,不仅仅是在类型注解 (type annotations)中。咱们不仅要学习类型自身,也要学习在什么中央应用这些类型产生新的构造。

咱们先温习下最根本和常见的类型,这些是构建更简单类型的根底。

原始类型: stringnumberboolean(The primitives)

JavaScript 有三个十分罕用的原始类型:stringnumberboolean,每一个类型在 TypeScript 中都有对应的类型。他们的名字跟你在 JavaScript 中应用 typeof 操作符失去的后果是一样的。

  • string 示意字符串,比方 “Hello, world”
  • number 示意数字,比方 42,JavaScript 中没有 int 或者 float,所有的数字,类型都是 number
  • boolean 示意布尔值,其实也就两个值:truefalse

    类型名 StringNumberBoolean(首字母大写)也是非法的,但它们是一些十分少见的非凡内置类型。所以类型总是应用 stringnumber 或者 boolean

数组(Array)

申明一个相似于 [1, 2, 3] 的数组类型,你须要用到语法 number[]。这个语法能够实用于任何类型(举个例子,string[] 示意一个字符串数组)。你也可能看到这种写法 Array<number>,是一样的。咱们会在泛型章节为大家介绍 T<U> 语法。

留神 [number]number[] 示意不同的意思,参考元组章节

any

TypeScript 有一个非凡的类型,any,当你不心愿一个值导致类型查看谬误的时候,就能够设置为 any

当一个值是 any 类型的时候,你能够获取它的任意属性 (也会被转为 any 类型),或者像函数一样调用它,把它赋值给一个任意类型的值,或者把任意类型的值赋值给它,再或者是其余语法正确的操作,都能够:

let obj: any = {x: 0};
// None of the following lines of code will throw compiler errors.
// Using `any` disables all further type checking, and it is assumed 
// you know the environment better than TypeScript.
obj.foo();
obj();
obj.bar = 100;
obj = "hello";
const n: number = obj;

当你不想写一个长长的类型代码,仅仅想让 TypeScript 晓得某段特定的代码是没有问题的,any 类型是很有用的。

noImplicitAny

如果你没有指定一个类型,TypeScript 也不能从上下文推断出它的类型,编译器就会默认设置为 any 类型。

如果你总是想防止这种状况,毕竟 TypeScript 对 any 不做类型查看,你能够开启编译项 noImplicitAny,当被隐式推断为 any 时,TypeScript 就会报错。

变量上的类型注解(Type Annotations on Variables)

当你应用 constvarlet 申明一个变量时,你能够选择性的增加一个类型注解,显式指定变量的类型:

let myName: string = "Alice";

TypeScript 并不应用“在右边进行类型申明”的模式,比方 int x = 0;类型注解往往跟在要被申明类型的内容前面。

不过大部分时候,这不是必须的。因为 TypeScript 会主动推断类型。举个例子,变量的类型能够基于初始值进行推断:

// No type annotation needed -- 'myName' inferred as type 'string'
let myName = "Alice";

大部分时候,你不须要学习推断的规定。如果你刚开始应用,尝试尽可能少的应用类型注解。你兴许会诧异于,TypeScript 仅仅须要很少的内容就能够齐全了解将要产生的事件。

函数(Function)

函数是 JavaScript 传递数据的次要办法。TypeScript 容许你指定函数的输出值和输入值的类型。

参数类型注解(Parameter Type Annotations)

当你申明一个函数的时候,你能够在每个参数前面增加一个类型注解,申明函数能够承受什么类型的参数。参数类型注解跟在参数名字前面:

// Parameter type annotation
function greet(name: string) {console.log("Hello," + name.toUpperCase() + "!!");
}

当参数有了类型注解的时候,TypeScript 便会查看函数的实参:

// Would be a runtime error if executed!
greet(42);
// Argument of type 'number' is not assignable to parameter of type 'string'.

即使你对参数没有做类型注解,TypeScript 仍然会查看传入参数的数量是否正确

返回值类型注解(Return Type Annotations)

你也能够增加返回值的类型注解。返回值的类型注解跟在参数列表前面:

function getFavoriteNumber(): number {return 26;}

跟变量类型注解一样,你也不须要总是增加返回值类型注解,TypeScript 会基于它的 return 语句推断函数的返回类型。像这个例子中,类型注解写和没写都是一样的,但一些代码库会显式指定返回值的类型,可能是因为须要编写文档,或者阻止意外批改,亦或者仅仅是集体爱好。

匿名函数(Anonymous Functions)

匿名函数有一点不同于函数申明,当 TypeScript 晓得一个匿名函数将被怎么调用的时候,匿名函数的参数会被主动的指定类型。

这是一个例子:

// No type annotations here, but TypeScript can spot the bug
const names = ["Alice", "Bob", "Eve"];
 
// Contextual typing for function
names.forEach(function (s) {console.log(s.toUppercase());
  // Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?
});
 
// Contextual typing also applies to arrow functions
names.forEach((s) => {console.log(s.toUppercase());
  // Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?
});

只管参数 s 并没有增加类型注解,但 TypeScript 依据 forEach 函数的类型,以及传入的数组的类型,最初推断出了 s 的类型。

这个过程被称为 上下文推断(contextual typing),因为正是从函数呈现的上下文中推断出了它应该有的类型。

跟推断规定一样,你也不须要学习它是如何产生的,只有晓得,它的确存在并帮忙你省掉某些并不需要的注解。前面,咱们还会看到更多这样的例子,理解一个值呈现的上下文是如何影响它的类型的。

对象类型(Object Types)

除了原始类型,最常见的类型就是对象类型了。定义一个对象类型,咱们只须要简略的列出它的属性和对应的类型。

举个例子:

// The parameter's type annotation is an object type
function printCoord(pt: { x: number; y: number}) {console.log("The coordinate's x value is " + pt.x);
  console.log("The coordinate's y value is " + pt.y);
}
printCoord({x: 3, y: 7});

这里,咱们给参数增加了一个类型,该类型有两个属性, xy,两个都是 number 类型。你能够应用 , 或者 ; 离开属性,最初一个属性的分隔符加不加都行。

每个属性对应的类型是可选的,如果你不指定,默认应用 any 类型。

可选属性(Optional Properties)

对象类型能够指定一些甚至所有的属性为可选的,你只须要在属性名后增加一个 ?

function printName(obj: { first: string; last?: string}) {// ...}
// Both OK
printName({first: "Bob"});
printName({first: "Alice", last: "Alisson"});

在 JavaScript 中,如果你获取一个不存在的属性,你会失去一个 undefined 而不是一个运行时谬误。因而,当你获取一个可选属性时,你须要在应用它前,先检查一下是否是 undefined

function printName(obj: { first: string; last?: string}) {
  // Error - might crash if 'obj.last' wasn't provided!
  console.log(obj.last.toUpperCase());
  // Object is possibly 'undefined'.
  if (obj.last !== undefined) {
    // OK
    console.log(obj.last.toUpperCase());
  }
 
  // A safe alternative using modern JavaScript syntax:
  console.log(obj.last?.toUpperCase());
}

联结类型(Union Types)

TypeScript 类型零碎容许你应用一系列的操作符,基于曾经存在的类型构建新的类型。当初咱们晓得如何编写一些根底的类型了,是时候把它们组合在一起了。

定义一个联结类型(Defining a Union Type)

第一种组合类型的形式是应用联结类型,一个联结类型是由两个或者更多类型组成的类型,示意值可能是这些类型中的任意一个。这其中每个类型都是联结类型的 成员(members)

让咱们写一个函数,用来解决字符串或者数字:

function printId(id: number | string) {console.log("Your ID is:" + id);
}
// OK
printId(101);
// OK
printId("202");
// Error
printId({myID: 22342});
// Argument of type '{myID: number;}' is not assignable to parameter of type 'string | number'.
// Type '{myID: number;}' is not assignable to type 'number'.

应用联结类型(Working with Union Types)

提供一个合乎联结类型的值很容易,你只须要提供合乎任意一个联结成员类型的值即可。那么在你有了一个联结类型的值后,你该怎么应用它呢?

TypeScript 会要求你做的事件,必须对每个联结的成员都是无效的。举个例子,如果你有一个联结类型 string | number , 你不能应用只存在 string 上的办法:

function printId(id: number | string) {console.log(id.toUpperCase());
    // Property 'toUpperCase' does not exist on type 'string | number'.
    // Property 'toUpperCase' does not exist on type 'number'.
}

解决方案是用代码收窄联结类型,就像你在 JavaScript 没有类型注解那样应用。当 TypeScript 能够依据代码的构造推断出一个更加具体的类型时,类型收窄就会呈现。

举个例子,TypeScript 晓得,对一个 string 类型的值应用 typeof 会返回字符串 "string"

function printId(id: number | string) {if (typeof id === "string") {
    // In this branch, id is of type 'string'
    console.log(id.toUpperCase());
  } else {
    // Here, id is of type 'number'
    console.log(id);
  }
}

再举一个例子,应用函数,比方 Array.isArray:

function welcomePeople(x: string[] | string) {if (Array.isArray(x)) {// Here: 'x' is 'string[]'
    console.log("Hello," + x.join("and"));
  } else {
    // Here: 'x' is 'string'
    console.log("Welcome lone traveler" + x);
  }
}

留神在 else分支,咱们并不需要做任何非凡的事件,如果 x 不是 string[],那么它肯定是 string .

有时候,如果联结类型里的每个成员都有一个属性,举个例子,数字和字符串都有 slice 办法,你就能够间接应用这个属性,而不必做类型收窄:

// Return type is inferred as number[] | string
function getFirstThree(x: number[] | string) {return x.slice(0, 3);
}

你可能很奇怪,为什么联结类型只能应用这些类型属性的交加,让咱们举个例子,当初有两个房间,一个房间都是身高八尺戴帽子的人,另外一个房间则是会讲西班牙语戴帽子的人,合并这两个房间后,咱们惟一晓得的事件是:每一个人都戴着帽子。

TypeScript 系列

  1. TypeScript 之 根底入门
  2. TypeScript 之 类型收窄
  3. TypeScript 之 函数
  4. TypeScript 之 对象类型
  5. TypeScript 之 泛型
  6. TypeScript 之 Keyof 操作符
  7. TypeScript 之 Typeof 操作符
  8. TypeScript 之 索引拜访类型
  9. TypeScript 之 条件类型

微信:「mqyqingfeng」,加我进冴羽惟一的读者群。

如果有谬误或者不谨严的中央,请务必给予斧正,非常感激。如果喜爱或者有所启发,欢送 star,对作者也是一种激励。

正文完
 0