本篇翻译整理自 TypeScript Handbook 中 「Everyday Types」 章节。
常见类型(Everyday Types)
本章我们会讲解 JavaScript 中最常见的一些类型,以及对应的描述方式。
类型可以出现在很多地方,不仅仅是在类型注解 (type annotations)中。我们不仅要学习类型本身,也要学习在什么地方使用这些类型产生新的结构。
我们先复习下最基本和常见的类型,这些是构建更复杂类型的基础。
原始类型(The primitives)
JavaScript 有三个非常常用的原始类型:string,number 和 boolean, 每一个类型在 TypeScript 中都有对应的类型。
他们的名字跟你在 JavaScript 中使用 typeof 操作符得到的结果是一样的。
类型名
String,Number和Boolean(首字母大写)也是合法的但它们是一些非常少见的特殊内置类型, 也就是所谓的包装类类型。
所以类型总是使用
string,number或者boolean。
数组(Array)
声明一个类似于 [1, 2, 3] 的数组类型,你需要用到语法 number[]。
这个语法可以适用于任何类型(举个例子,string[] 表示一个字符串数组)。
你也可能看到这种写法 Array<number>,在功能上是一样的。
注意
[number]和number[]表示不同的数据类型
[number]表示的是元组类型
number[]表示的是数组类型
any
TypeScript 有一个特殊的类型,any,当你不希望一个值导致类型检查错误的时候,就可以设置为 any 。
当一个值是 any 类型的时候,你可以获取它的任意属性 (也会被转为 any 类型),或者像函数一样调用它,把它赋值给一个任意类型的值,或者把任意类型的值赋值给它,再或者是其他语法正确的操作,都可以:
let obj: any = { x: 0 };
// 以下所有代码都是合法的
obj.foo();
obj();
obj.bar = 100;
obj = "hello";
const n: number = obj;
如果你没有指定一个类型,TypeScript 也不能从上下文推断出它的类型,编译器就会默认设置为 any 类型。
当你不想写长长的类型代码,仅仅想让 TypeScript 知道某段特定的代码是没有问题的,any 类型是很有用的。
尤其是你将JavaScript代码转移成TypeScript代码的时候
但是对于新的代码,并不推荐使用any。为此我们可以开启编译项 noImplicitAny
当被隐式推断为 any 时,TypeScript 就会报错。
变量上的类型注解(Type Annotations on Variables)
当我们声明一个变量时,你可以选择性的添加一个类型注解,显式指定变量的类型
let myName: string = "Klaus";
不过大部分时候,这不是必须的。因为 TypeScript 会自动推断类型
// myName的类型会被自动推测为string
let myName = "Klaus";
函数(Function)
函数是 JavaScript 传递数据的主要方法。TypeScript 允许你指定函数的输入值和输出值的类型。
参数类型注解(Parameter Type Annotations)
当你声明一个函数的时候,你可以在每个参数后面添加一个类型注解,声明函数可以接受什么类型的参数。参数类型注解跟在参数名字后面
function greet(name: string) {
console.log("Hello, " + name.toUpperCase() + "!!");
}
即便你对参数没有做类型注解,TypeScript 依然会检查传入参数的数量是否正确
返回值类型注解(Return Type Annotations)
返回值的类型注解跟在参数列表后面:
function getFavoriteNumber(): number {
return 26;
}
跟变量类型注解一样,你也不需要总是添加返回值类型注解,TypeScript 会基于它的 return 语句推断函数的返回类型。
匿名函数(Anonymous Functions)
匿名函数有一点不同于函数声明,当 TypeScript 知道一个匿名函数将被怎样调用的时候(例如作为回调函数)
匿名函数的参数会被自动的指定类型。
const names = ["Alice", "Bob", "Eve"];
// 上下文推断(contextual typing)
names.forEach(name => console.log(name.toUppercase();
// Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?
});
对象类型(Object Types)
除了原始类型,最常见的类型就是对象类型了
TypeScript是结构性类型检测,我们只需要简单的列出它的属性和对应的类型即可
// 只要存在必要的属性,就是可以兼容的结构
// 类型有两个属性, x 和 y,两个都是 number 类型。你可以使用 , 或者 ; 分开属性,最后一个属性的分隔符加不加都行
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 });
可选属性(Optional Properties)
对象类型可以指定一些甚至所有的属性为可选的,你只需要在属性名后添加一个 ?
function printName(obj: { first: string; last?: string }) {
// ...
}
// Both OK
printName({ first: "Bob" });
printName({ first: "Alice", last: "Alisson" });
联合类型(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 });
使用联合类型(Working with Union Types)
TypeScript 会要求你做的事情,必须对每个联合的成员都是有效的。也就是我们只能使用联合类型上公共的属性和方法。
例如如果你有一个联合类型 string | number , 你不能使用只存在 string 上的方法
如果我们需要访问某一个具体的类型上的属性或方法。解决方案是用代码收窄联合类型,当 TypeScript 可以根据代码的结构推断出一个更加具体的类型时,类型收窄就会出现。
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);
}
}
有时候,如果联合类型里的每个成员都有一个属性,举个例子,数字和字符串都有 slice 方法,你就可以直接使用这个属性,而不用做类型收窄:
// Return type is inferred as number[] | string
function getFirstThree(x: number[] | string) {
return x.slice(0, 3);
}