基础知识
日常类型
- 基础类型:string,number和boolean
类型名称 String、Number 和 Boolean(以大写字母开头)是合法的,但指的是一些很少出现在代码中的特殊内置类型。
始终使用 string、number 或 boolean 作为类型。
- 数组
请注意,[number] 是另一回事;请参阅 元组 部分
- any
任何类型的值,或者几乎任何其他东西在any语法上是合法的
- 变量的类型注释
let myName: string = "Alice";
- 函数:指定函数的输入和输出值的类型
- 参数类型注解:在每个参数后面加上类型注解,声明函数接受哪些类型的参数
// Parameter type annotation function greet(name: string) { console.log("Hello, " + name.toUpperCase() + "!!"); }
- 返回类型注解:添加返回类型注释。返回类型注释出现在参数列表之后
function getFavoriteNumber(): number { return 26; }
- 返回 Promise 的函数:想注释一个返回 Promise 的函数的返回类型,你应该使用 Promise 类型
async function getFavoriteNumber(): Promise<number> { return 26 }
- 匿名函数
[类型推断]: 来确定 s 将具有的类型 称为上下文类型,因为函数发生的上下文告知它应该具有什么类型。
- 对象类型:除了基础类型外,最常见的就是对象类型 【 ; 】 来分隔属性
// 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 });
- 可选属性:指定部分或全部属性是可选的,请在属性名称后添加 ?
function printName(obj: { first: string; last?: string }) { // ... } // Both OK printName({ first: "Bob" }); printName({ first: "Alice", last: "Alisson" });
注意:当你从可选属性中读取数据时,你必须在使用它之前检查 undefined。
function printName(obj: { first: string; last?: string }) { // Error - might crash if 'obj.last' wasn't provided! console.log(obj.last.toUpperCase()); 'obj.last' 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()); }
- 联合类型:用各种运算符从现有类型中组合他们
- 定义联合类型:由两种或多种其他类型组成的类型,表示可能是这些类型中的任何一种的值
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'.
- 使用联合类型 :提供与任何联合成员匹配的类型即可
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'. }
注意:用代码缩小联合
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); } }
类型的联合似乎具有这些类型的属性的交叉,这可能会令人困惑。这不是意外 - union 这个名字来自于类型理论。联合 number | string 是通过取每种类型的值的联合组成的。请注意,给定两个具有关于每个集合的相应事实的集合,只有这些事实的交叉适用于集合本身的并集。例如,如果我们有一个房间里有戴帽子的高个子,而另一个房间里有戴帽子的说西班牙语的人,在组合这些房间后,我们对每个人的唯一了解就是他们必须戴帽子。
- 类型别名【type Name = {}】:任何类型的名称,希望多次使用同一个类型并用一个名称引用它
type Point = {
x: number;
y: number;
};
// Exactly the same as the earlier example
function printCoord(pt: Point) {
console.log("The coordinate's x value is " + pt.x);
console.log("The coordinate's y value is " + pt.y);
}
printCoord({ x: 100, y: 100 });
类型别名可以为任何类型命名,可以命名联合类型
type ID = number | string;
注意,别名只是别名 - 你不能使用类型别名来创建相同类型的不同/独特的 “版本”。
type UserInputSanitizedString = string;
function sanitizeInput(str: string): UserInputSanitizedString {
return sanitize(str);
}
// Create a sanitized input
let userInput = sanitizeInput(getInput());
// Can still be re-assigned with a string though
userInput = "new input";
这段代码可能看起来非法,但根据 TypeScript 是可以的,因为这两种类型都是同一类型的别名
- 接口【interfaces】:是命名对象类型的另一种方式
interface Point {
x: number;
y: number;
}
function printCoord(pt: Point) {
console.log("The coordinate's x value is " + pt.x);
console.log("The coordinate's y value is " + pt.y);
}
printCoord({ x: 100, y: 100 });
ts结构类型类型系统:只关心类型的结构和功能
- 类型别名和接口的区别
1.类型别名和接口非常相似,在很多情况下你可以在它们之间自由选择
2.interface 的几乎所有功能都在 type 中可用- 区别:
1.类型【type】无法重新打开以添加新属性,仅可通过【交叉扩展类型】添加
2.接口【interface】始终可扩展新属性,a.可以【扩展接口extends】添加;b. 向现有接口添加新字段
Interface
扩展接口【extends】
interface Animal { name: string; } interface Bear extends Animal { honey: boolean; } const bear = getBear(); bear.name; bear.honey;
向现有接口添加新字段
interface Window { title: string; } interface Window { ts: TypeScriptAPI; } const src = 'const a = "Hello World"'; window.ts.transpileModule(src, {});
Type
通过交叉扩展类型 【&】
type Animal = { name: string; } type Bear = Animal & { honey: boolean; } const bear = getBear(); bear.name; bear.honey;
类型创建后无法更改
type Window = { title: string; } type Window = { ts: TypeScriptAPI; } // Error: Duplicate identifier 'Window'.
区别总结:在大多数情况下,可以根据个人喜好进行选择,想要启发式方法,请使用 interface
-
在 TypeScript 4.2 版之前,类型别名 可能出现在错误信息中,有时代替等效的匿名类型(可能需要也可能不需要)。
接口将始终在错误消息中命名。 -
类型别名不得参与在声明合并中,但接口可以参与在声明合并中。
-
接口只能用于 声明对象的形状,而不是重命名基础类型。
-
接口名称将在错误消息中显示为 总是以原来的形式出现,但仅当它们被名称使用时。
- 类型断言【as】:对于 ts 无法知道的值类型,可以使用类型断言【as】来指定其具体的类型
1.可以使用类型断言【as】来指定更具体的类型
const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;
2.还可以使用尖括号语法【<>】(除非代码在 .tsx 文件中),它是等效的:
//You can also use the angle-bracket syntax (except if the code is in a .tsx file), which is equivalent:
const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas");
提醒:因为类型断言在编译时被删除,所以没有与类型断言关联的运行时检查。如果类型断言错误,则不会产生异常或 null。
有时,此规则可能过于保守,并且不允许可能有效的更复杂的强制转换。如果发生这种情况,你可以使用两个断言,首先是 any(或 unknown,我们稍后会介绍),然后是所需的类型
const a = expr as any as T;
- 字面类型:除了通用类型 string 和 number 之外,我们还可以在类型位置引用特定的字符串和数字
就其本身而言,字面类型并不是很有价值,变量只能有一个值并没有多大用处!
let x: "hello" = "hello";
// OK
x = "hello";
// ...
x = "howdy";
//Type '"howdy"' is not assignable to type '"hello"'.
但将【字面量组合成联合】,你可以表达更有用的概念
function printText(s: string, alignment: "left" | "right" | "center") {
// ...
}
printText("Hello, world", "left");
printText("G'day, mate", "centre");
//Argument of type '"centre"' is not assignable to parameter of type '"left" | "right" | "center"'.
数字字面类型的工作方式相同:
function compare(a: string, b: string): -1 | 0 | 1 {
return a === b ? 0 : a > b ? 1 : -1;
}
将这些与非字面类型结合使用
interface Options {
width: number;
}
function configure(x: Options | "auto") {
// ...
}
configure({ width: 100 });
configure("auto");
configure("automatic");
Argument of type '"automatic"' is not assignable to parameter of type 'Options | "auto"'.
- 字面推断 :当你使用对象初始化变量时,TypeScript 假定该对象的属性可能会在以后更改值。
declare function handleRequest(url: string, method: "GET" | "POST"): void;
const req = { url: "https://example.com", method: "GET" };
handleRequest(req.url, req.method);
// 错误 Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.
as const
:将整个对象转换为类型字面
const req = { url: "https://example.com", method: "GET" } as const;
handleRequest(req.url, req.method);
as
通过在任一位置添加类型断言来更改推断const req = { url: "https://example.com", method: "GET" as "GET" };
handleRequest(req.url, req.method as "GET");
null 和 undefined
表示值不存在或未初始化的值:null 和 undefined。
- 非空断言运算符(后缀 !)
在任何表达式之后写 ! 实际上是一个类型断言,该值不是 null 或 undefined
function liveDangerously(x?: number | null) { // No error console.log(x!.toFixed()); }
就像其他类型断言一样,这不会改变代码的运行时行为,所以当你知道值不能是 null 或 undefined 时,只使用 ! 很重要。
枚举 【Enums】
允许描述一个值,该值可能是一组可能的命名常量之一
不是对 JS 的类型级添加,而是添加到语言和运行时的东西
不太常见的基础类型
- bigint:用于非常大的整数
- symbol:用于通过函数 Symbol() 创建全局唯一引用