你好,我是King。
今天咱们聊一个实际开发中经常遇到的烦心事。
先说说这个痛点
你有没有遇到过这种情况:
同事把后端返回的数据结构改了,原来叫 userName,现在改成 fullName。然后你的代码里到处是 userName,一编译,全是红色波浪线。你得一个一个文件去找,去改,生怕漏掉一个。
这种痛苦,我相信写 TypeScript 的朋友都经历过。
核心问题出在哪?
说白了,就是我们写的代码和对象结构之间的“关联”太脆弱了。
比如你写:
function getUserName(user: any) {
return user.userName
}这里的 any 就是罪魁祸首。用了 any,TypeScript 就像被蒙上了眼睛,你跟它说“放心吧,这没问题”,它就真的不管了。等数据一变化,它就傻眼了。
主角登场:keyof + extends
这两个关键词,看着高大上,其实大白话解释一下,特别简单。
keyof 是什么?
keyof 翻译成大白话就是:“把对象里的所有属性名拿出来,做成一个列表”。
举个例子:
interface User {
id: number
name: string
age: number
}
type UserKeys = keyof User // 结果相当于: 'id' | 'name' | 'age'看到那个 | 了吗?意思是“或者”。UserKeys 这个类型,只能是 'id' 或者 'name' 或者 'age' 这三个字符串之一。多一个不行,少一个也不行。
extends 在这里怎么理解?
在泛型约束里,extends 不是“继承”,而是 “必须是……的成员”。
看这段代码:
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key]
}这里的 K extends keyof T 翻译成人话就是:“参数 K 必须是 T 这个对象里存在的属性名之一”。
你说传 getProperty(user, 'phone'),而 user 对象里没有 phone 这个属性,TypeScript 马上报错,根本不让代码通过。
实际案例:重构不再害怕
假设你有这样一个对象:
interface Product {
title: string
price: number
stock: number
}你写了一个通用方法:
function updateProductField<T, K extends keyof T>(
product: T,
field: K,
value: T[K]
) {
product[field] = value
}这里 T[K] 的意思是:“对象 T 中属性 K 对应的值类型”。
比如你调用 updateProductField(myProduct, 'price', 100),TypeScript 会自动推断出 price 是 number 类型。你要是传 100 进去没问题,传 'abc' 进去,直接报错。
更厉害的是后面:
你把 Product 接口里的 price 改成 costPrice:
interface Product {
title: string
costPrice: number // 改这里
stock: number
}然后你之前写的代码里还有 updateProductField(myProduct, 'price', 100),TypeScript 会立刻告诉你:'price' 不在 Product 的属性列表里了。
你根本不用自己去翻代码找哪里用了 price,编译器帮你全找出来了。你只需要跟着错误提示,一个个改掉就行。
一个更实用的场景
实际项目中,我们经常要写更新对象的函数:
function patchObject<T extends object>(
original: T,
updates: Partial<T>
): T {
return { ...original, ...updates }
}这里 Partial<T> 的意思是:把 T 的所有属性都变成可选的。
调用的时候:
const user = { id: 1, name: 'King', age: 28 }
const newUser = patchObject(user, { name: 'King New' })如果后来你把 name 改成了 nickname,那么 patchObject(user, { name: '...' }) 这一行立马报错。不需要等你上线后出 bug,写代码的瞬间就知道了。
日常写代码的三个小习惯
想彻底告别属性重构带来的恐惧,记住这三点:
永远不要用 any,除非你特别确定自己在做什么。用 unknown 都比 any 强。
定义接口后,所有操作这个对象的函数,都加上泛型约束。多用 keyof,多用 extends。
用好 IDE 的重构功能。当你需要改一个属性名时,用 IDE 的“重命名”功能(F2),TypeScript 会自动帮你把所有关联的地方一起改掉。
总结一下
keyof 是“提取属性名列表”,extends 在泛型里是“必须是其中的一个”。两个组合在一起,就实现了一个效果:
当你改了一个地方,TypeScript 会帮你找出所有需要跟着改的地方。
这不是什么黑科技,就是 TypeScript 早就有的能力。只不过很多人习惯用 any,把这个能力给浪费了。
从现在开始,试着把代码里的 any 都换成准确的类型,用上 keyof 和 extends。下次再改属性结构,你就不慌了。
希望你从今天开始,写代码更从容一点。
我是King,下期见。
更多内容分享链接:
https://www.kkkmir.com/xkcq/347.html
https://www.sofuba.com/xkcq/347.html
https://www.haomir.com.cn/xkcq/347.html
https://www.kfzhan.com/bbdq/288.html
https://www.kfzhan.com/bbdq/290.html
https://www.kfzhan.com/bbdq/289.html
https://www.kfzhan.com/bbdq/291.html
https://www.kfzhan.com/bbdq/292.html
https://www.kfzhan.com/bbdq/293.html
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用。你还可以使用@来通知其他用户。