持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 1 天
深入对象语法
对象可以用 type 和 interface 来描述
// 表达一种含有一些某些属性的对象
// 以下 Person1/2/3 都等价
type Person1 = {
name: string
}
// 以下 Person2 所有的类型都是这个 Person1 的子集
type Person2 =
| { name: string, age: string }
| { name: string, age: number }
| { name: string, age: boolean }
| { name: string, age: null }
| { name: string, age: undefined }
| { name: string, age: object }
| { name: string, age: string }
| { name: string, age: number }
// 以下省略一万行....
interface Person3 {
name: string
}
索引签名 Index Signature
// 这里的 k 不一定要叫 k, 满足变量名要求即可
type Hash = {
[k: string]: unknown
length: number
}
type List = {
[k: number]: unknown
length: number
}
映射类型 Mapped Type (多用于范型)
// in 主要是用于范型
type Hash = {
[k in string]: unknown
}
type List = {
[k in number]: unknown
}
写成以上写法会产生以下报错
问号表示可选
interface InputProps {
defaultValue?: string
value?: string
//value1: string | undefined
onChange?: () => void
}
// 这里的可选是不是等价于 它可以是 undefined ?
const props: InputProps = {
value: undefined,
// value1: undefined,
defaultValue: 'hi',
onChange: ()=> 1
}
// 可以的,也就是说 ? 是可以給它赋值为 undefined
// 但是用 undefined 代表 ? 它表达的意思就不一样
所以为了避免麻烦,优先使用 ?
readonly 表示只读,不能写
interface User {
readonly id: number
readonly name: string
readonly scores: number[]
age?: number
}
const u: User = {
id: 1,
name: 'hone',
scores: [87, 65]
}
u.id = 2
u.scores = [100, 100]
// 改 scores 对应的下标 不报错
// 这说明 这个readonly 只会管 scores 这一层,它的下一层不管
u.scores[0] = 100
、
readonly 可以保证它后面的属性不可写,但是并不保证这个属性里面的属性不可写。
函数就像对象一样
对象的语法全都适用于函数
- type 或 interface
// 如果想在函数上加点东西,就用以下这种写法
type F = {
(a: number, b: number): number
// 比如说这个函数上有一个属性
count: number
}
// F 等价于 F1
// 如果只是想声明一个函数就用以下这种写法
type F1 = (a: number, b: number) => number
const f: F = (x, y) => {
return x + y
}
f.count = 1
interface F {
(a: number, b: number): number
count: number
}
// F 等价于 F1
type F1 = (a: number, b: number) => number
const f: F = (x, y) => {
return x + y
}
f.count = 1
-
索引签名 和 映射类型
-
问号表示可选
interface F {
(a: number, b: number): number
count?: number
}
const f: F = (x, y) => {
return x + y
}
// 这个 count 就是可有可无的
- readonly 表示只读
interface F {
(a: number, b: number): number
readonly count?: number
}
const f: F = (x, y) => {
return x + y
}
声明函数的4种方式
第一种:先写类型再赋值
type F1 = (a: number, b: number) => number
const f1 = F1 = (a,b) => a + b
第二种:先实现箭头函数,再获取类型
const f2 = (a: number, b: number): number => {
return a + b
}
type F2 = typeof f2
第三种:先实现普通函数,再获取类型
function f3(this: unknown, a: number, b: number): number {
return a + b
}
type F3 = typeof f3
第四种:先实现匿名普通函数,再获取类型
const f4 = function (this: unknown, a: number, b: number): number {
return a + b
}
type F4 = typeof f4
第五种: 写的很少,除非写库
const f5 = new Function('a', 'b', 'return a + b')
type F5 = typeof f5
暂不讨论 生成器和异步函数
类型谓词
// 自带的类型比较好收窄
function f1(a: string | string[]) {
// 这个时候我们在用 a 的时候就需要做类型收窄
if (a instanceof Array) {
a // Array
} else {
a // string
}
}
type Person = {
name: string
}
type Animal = {}
// 如果还有其它的类型,就不太好收窄了
// function f1(a: Person | Animal) {}
// 那怎么办呢?
// 我们就需要借助 isPerson
// 这里的 boolean 不写也会自动补全的
function isPerson(x: Person | Animal): boolean {
if ('name' in x) {
return true
} else {
return false
}
}
// 这样的话我们就可以使用
function f1(a: Person | Animal) {
if (isPerson(a)) {
a // 能不能收窄?没有收窄
}
}
问题在于 isPerson 这个函数和 Person 这个类型,没有任何关系。
所以得要想办法让这两个东西,建立关联,那么如何建立关联?
// 把 boolean 写成 Person
function isPerson(x: Person | Animal): Person {
if ('name' in x) {
return true
} else {
return false
}
}
但是写上 Person 就有问题,说明返回的是一个 Person
写成 is Person 也不行,因为你没有告诉我,哪个变量是 Person
所以就把这个变量名写到后面来
// x is Person 的意思是:我的返回值确实是一个 boolean
// 但是这个 boolean 是关系到 x 是不是 Person
function isPerson(x: Person | Animal): x is Person {
return 'name' in x
}
这个所谓的收窄只是在写代码的时候,做代码提示的时候要收窄,运行的时候只需要有这个属性就行了,不管收窄的(运行的时候只管逻辑不管类型)。
以上代码为何不能用箭头函数呢?因为箭头函数不能给名字。
const isPerson2 =(x: Person | Animal) => {
return 'name' in x
}
发现并没有收窄
因为没有关键的 is Person
但是问题在于,isPerson 有两个写法
写在右边没有问题
写在左边为什么会报错?
type A = (x: Person | Animal) => x is Person
const isPerson2: A = (x)=> 'name' in x
// ()=>x is Person ()=>boolean
// boolean 是不能赋值给 x is Person 的
// 除非写成以下写法
// const isPerson2: A = (x):x is Person=> 'name' in x
// 这里 x is Person 写了两遍,那还不如全写在右边
问题在于:boolean 是不能变成 is 的,TS 以为 (x)=> 'name' in x 这个是 boolean, 但是 boolean 和 is 不是等价的,所以就错了。
所以用类型谓词尽量用 function, 少用箭头函数,就不会存在纠结了。
参数相关语法
可选参数
// useCapture 如果不传它就是 undefined
function addEventListener(
eventType: string, fn: unknown, useCapture?: boolean
) {
console.log(eventType, fn, useCapture)
}
addEventListener('click', ()=>1)
参数默认值
// 写成 useCapture = false 表示·参数默认值是 false
function addEventListener(
eventType: string, fn: unknown, useCapture = false
) {
console.log(eventType, fn, useCapture)
}
addEventListener('click', ()=>1)
参数也是函数
// 在 TS 里是不分是不是箭头函数的,只分能不能加 this
// (this: HTMLElement, e: Event) => void 这个代表它是一个函数,不能代表它是一个箭头函数
function addEventListener(
eventType: string, fn: (this: HTMLElement, e: Event) => void, useCapture?: boolean
) {
// 伪代码
const element = {} as HTMLElement
const event = {} as Event
// 以上先构造所有需要的对象然后传给下面
fn.call(element, event)
console.log(eventType, fn, useCapture)
}
addEventListener('click', ()=>1)
函数柯里化
返回值也是函数
const add = (a: number, b: number) => a + b
const createAdd => (a: number) => (b: number) => a + b
createAdd(6)(14) // 20
// 把类型和值分开写
type CreateAdd = (x: number) => (y: number) => number
const createAdd: CreateAdd = a => b => a + b