日常类型
函数类型
参数类型注释
function greet(name: string) {
console.log('hello', name);
}
返回值注释
function returnStr(): string {
return '123'
}
返回Promise
async function getFavoriteNumber(): Promise<number> {
return 26
}
匿名函数
会根据上下文推断参数类型
const names: string[] = ['Aice', 'Bob', 'Eve']
names.map(s => {
console.log(s.toLocaleLowerCase()); //编辑器会提示相应(string)的属性和方法
})
对象类型
function printName(obj: { first: string; last?:string }) {
// ...
}
联合类型
联合类型是由两种或多种其他类型组成的类型,表示可能是这些类型中的任何一种的值
function printId(id: number | string) {
console.log(id);
}
使用联合类型 提供与联合类型匹配的值很容易 - 只需提供与联合的任何成员匹配的类型即可。 如果你有一个联合类型的值,你如何处理它? TypeScript 只有在对联合的每个成员都有效的情况下才允许操作。 例如,如果你有联合 string | number,则不能使用仅在 string 上可用的方法:
function printId(id: number | string) {
// 这里不会提示 id 有 toUpperCase()方法,因为这个方法属于 string 类型
console.log(id.toUpperCase());
}
解决方案是用代码缩小联合,就像在没有类型注释的 JavaScript 中一样。 当 TypeScript 可以根据代码的结构为某个值推断出更具体的类型时,就会发生缩小。
function printId(id: number | string) {
if (typeof id === 'string') {
console.log(id.toUpperCase()); // 这里会有提示
}
}
类型别名
type Point = {
x: number;
y: number
}
function printCoord(pt: Point) {
console.log(pt.x, pt.y);
}
printCoord({x: 1, y: 2})
type ID = number | string
接口
interface Name {
first: string;
last?: string
}
interface Name {
fullName: string
}
类型别名(type)和接口(interface)的差别
类型别名和接口非常相似,在很多情况下你可以在它们之间自由选择。 interface
的几乎所有功能都在 type
中可用,主要区别在于type
无法再次声明,和添加新属性,而interface
始终可扩展。
添加新字段
// interface
// 向现有接口添加新字段
// 不会被先后顺序影响,有点像js的作用域提升。
interface Animal { // 注意: 这里没用 `=`
name: string
}
interface Animal {
honey: boolean
}
// type
// 类型创建后无法更改
type Person = { // 注意: 这里有 `=`
name: string,
}
type Person = { // Error: Duplicate identifier 'Window'.
age: number | string
}
扩展
// interface
// 通过继承
interface Animal {
name: string;
}
interface Bear extends Animal {
honey: boolean;
}
const bear = getBear();
bear.name;
bear.honey;
// type
// 通过交集
type Animal = {
name: string;
}
type Bear = Animal & {
honey: boolean;
}
const bear = getBear();
bear.name;
bear.honey;
类型断言
有时你会得到关于 TypeScript 无法知道的值类型的信息。
例如,如果你使用的是 document.getElementById,TypeScript 只知道这将返回某种 HTMLElement,但你可能知道你的页面将始终具有具有给定 ID 的 HTMLCanvasElement。
在这种情况下,你可以使用类型断言来指定更具体的类型:
const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;
TypeScript 只允许类型断言转换为更具体或更不具体的类型版本。 此规则可防止 “impossible” 强制,例如:
const x = "hello" as number;
// Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
有时,此规则可能过于保守,并且不允许可能有效的更复杂的强制转换。 如果发生这种情况,你可以使用两个断言,首先是 any(或 unknown,我们稍后会介绍),然后是所需的类型:
const a = expr as any as T;
字面类型
除了string
、number
这些JavaScript类型外,我们还可以在类型位置使用特点的字符串和数字。
这和用const
声明常量有点相似,
// a和b都无法在修改成别的字符串
const a: 'hello' = 'hello'
let b: 'world' = 'world'
interface Position {
alignment: 'left' | 'right',
}
function printText(position: Position) {
// ...
}
printText({ alignment: 'left' })
字面推断
当你使用对象初始化变量时,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"'.
在上面的例子中, req.method
被推断为 string
,而不是 "GET
"。 因为可以在 req
的创建和 handleRequest
的调用之间评估代码,这可以将一个新的字符串(如 "GUESS
" 分配给 req.method
),TypeScript 认为此代码有错误。
以下方法可以解决这个问题
//change1
// const req = { url: 'https://example.com', method: 'GET' as 'GET' }
//change2
// handleRequest(req.url, req.method as 'GET')
//change3
// const req = { url: 'https://example.com', method: 'GET' } as const