基础类型:
为了有效地工作,我们常常需要与最简单的数据类型打交道:数值,字符串,布尔值等等。在Ts里面,我们不仅支持与JS同样的类型,还会需要一个额外的可枚举的属性规范当前值的类型。
Boolean
最基础的数据类型就是boolean值(true / false):
let isDone: boolean = false;
Number
在JS中,数值可以是整型或者浮点型。在TS中,浮点数我们仍旧使用number,而大整数我们使用bigint来定义。此外,TS也支持给数值型变量赋2进制、8进制、16进制类型的值。
let decimal: number = 6;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;
let big: bigint = 100n;
String
在网页和服务器中JS的另一个基础部分就是文本数据。和其他语言相似,我们使用string来代表这种类型。在TS中我们也支持这点。
let color: string = "blue";
此外,我们可以使用模板字符串(支持多行和嵌入JS变量、表达式)。
let fullName: string = `Bob Bobbington`;
let age: number = 37;
let sentence: string = `Hello, my name is ${fullName}.
I'll be ${age + 1} years old next month.`;
Array
类似JS,TS也支持数组格式。在TS中数组有两种写法:第一种是定义数组中的值的类型后加一个[]来创建数组:
let list: number[] = [1, 2, 3];
第二种是使用通用的数组类型,Array<elemType>:
let list: Array<number> = [1, 2, 3];
Tuple
tuple(元组)代表指定一个固定长度和数组值类型(以及顺序)的数组。
let x: [string, number]; // this is a tuple type
x = ["hello", 10]; // OK
x = [10, "hello"]; // Error
Error:
Type 'number' is not assignable to type 'string'.
Type 'string' is not assignable to type 'number'.
如果尝试对一个已知索引的数组值做操作,TS会进行类型检索:
console.log(x[0].substring(1)); // ok
console.log(x[1].substring(1)); // ERROR
// Property 'substring' does not exist on type 'number'.
倘若试图增加元组的长度/读取超出预期的数组元素,编译的时候会返回一个Error(但是操作实际上也许会成功——我的意思是,起码在tsc中没有问题:但是TS真的不推荐编程者这么写)
x[3] = "world";
// Error: Tuple type '[string, number]' of length '2' has no element at index '3'.
console.log(x[5].toString());
// Error: Object is possibly 'undefined'.
// Tuple type '[string, number]' of length '2' has no element at index '5'.
Enum
enum是TS添加的一个数据类型的标准集合。和C#一样,enum一般用于为数值集合一个更好的体现。(简而言之,枚举而已)
enum Color {
Red,
Green,
Blue,
}
let c: Color = Color.Green;
一般情况下,枚举元素的值默认从0开始。你可以通过显式赋值来改变默认值(另外,未被赋值的元素的值为前者+1)。
enum有一个方便功能:你可以从一个数字的值获取到枚举中那个值的name.
enum Color {
Red = 1,
Green,
Blue,
}
let colorName: string = Color[2];
// Displays 'Green'
console.log(colorName);
Unknown
当我们在写一个应用的时候 我们可能会遇上这种情况: 我们需要给一个未知类型的变量定义并赋予其一个属性。比如,这些值可能从用户那里动态获取或者我们通过异步调用API获取到的数据。在这种情况下,我们提供了一个新的类型供开发者使用,表明当前变量的类型可能是任意值。
let notSure: unknown = 4;
notSure = "maybe a string instead";
// OK, definitely a boolean
notSure = false;
如果你定义了未知类型的变量,你可以通过typeof方法或直接赋值的方式从而将该变量赋予其他已经固定了类型的变量:
declare const maybe: unknown
const aNumber: number = maybe;
// Error: Type 'unknown' is not assignable to type 'number'.
if (maybe === true) {
const aBoolean: boolean = maybe // OK
const aString: string = maybe // absolutely no way
}
if (typeof maybe === 'string') {
const bString: string = maybe // Ok
const bBoolean: boolean = maybe // NO
}
Any
在某些情况下,有些信息有时不能得到,有时本身的声明会产生相当的不当影响。一般情况下在引入第三方库或者非ts文件中返回的时候会有这种情况。因此,我们需要使用any放弃类型检查。
let looselyTyped: any = 4;
// OK, ifItExists might exist at runtime
looselyTyped.ifItExists();
// OK, toFixed exists (but the compiler doesn't check)
looselyTyped.toFixed();
let strictlyTyped: unknown = 4;
strictlyTyped.toFixed();
// Object is of type 'unknown'.
any类型的值可以传递下去
let looselyTyped: any = {};
let d = looselyTyped.a.b.c.d;
// ^ = let d: any
总之,any在带来便利的同时牺牲了数据类型的安全性。数据类型的安全是我们使用TS的核心动力之一,我们要尽量避免使用any.
Void
void感觉有点像any的反面:根本没有任何返回值。你可能经常会在无返回值的函数中看到他:
function warnUser(): void {
console.log("This is my warning message");
}
声明一个变量的类型是void实际上是无效的,因为你实际上仅仅只有null / undefined 这个对象符合.
Null and undefined
在TS中,我们单独为null和undefined两种类型提供了单独的类型描述。和void一样,这两者基本上没什么用:
let u: undefined = undefined;
let n: null = null;
null和undefined默认是其他类型的亚型,这意味着你可以把null和undefined赋值给其他类型的变量。
但是当我们使用--strickNullChecks标志的时候,这两个值只能被赋值给unknown, any类型和相似的类型(void之类的)。这能避免很多常见的问题。如果你在某些情形中需要创建一个可能是string、undefined或者null其中之一的类型的变量,在声明变量的时候可以定义它为undefined | string | null.
联合版本是一个好用的点子,之后我们会详述。
另外,我们推荐在TS中编程时使用--strickNullChecks.
Never
我们一般用Never来定义那些绝不可能出现的值。这常用于定义部分用来抛出错误的函数。在某些情况下,当变量被永不可能实现的条件限制时也可用Never定义。
Never类型是所有类型的亚型,但是没有一个类型是never的亚型,即使是any也不行。
function error(message: string): never {
throw new Error(message);
}
// Inferred return type is never
function fail() {
return error("Something failed");
}
// Function returning never must not have a reachable end point
function infiniteLoop(): never {
while (true) {}
}
Object
object是非基础类型,即任何不是number,string,boolean,bigint,symbol,null和undefined的东西。
利用object类型定义,像Object.create这样的API可以更方便的调用。例如:
declare function create(o: object | null): void;
// OK
create({ prop: 0 });
create(null);
create(42); //Argument of type '42' is not assignable to parameter of type 'object | null'.
create("string"); //Argument of type '"string"' is not assignable to parameter of type 'object | null'.
事实上你基本上不需要这个。
类型断言
有时你会遇到这种情况:你知道这个值的最终结果/类型/部分信息。
类型断言是用来通知编译器:‘我知道我在做什么,相信我’。这个功能像个演员一样,但是他不会重组数据或特殊的数据检查。它本身不存在运行时间的问题,纯粹的被编辑器使用着。TS认为你作为一个开发者已经做好了类型检测了。
类型断言有两种格式:
一种是as:
let someValue: unknown = "this is a string";
let strLength: number = (someValue as string).length;
另一种是使用<>:
let someValue: unknown = "this is a string";
let strLength: number = (<string>someValue).length;
两者有异曲同工之妙。使用哪一种都随开发者喜好,但是在TS中只有前者是可用的。
关于let:你可能已经注意到了,到目前为止,相对于我们或许更熟悉的var,我们一直在使用let来定义变量。实际上关键词let在TS中提供的一个新的JS构造。
关于Number, String, Boolean, Symbol和Object: 一般大家会默认的以为这几个类型和之前我们提到的number,string等是同一个类型,但是实际上并不是这样,而且我们极不推荐设定这些值为一种类型。