有没有办法为 Lodash 的 set 函数添加 TypeScript 类型安全?
我需要为深度嵌套的对象设置值。属性可以是未定义的,我不想覆盖其他对象属性。
此代码有效:
import set from "lodash/set";
type Res = {
position?: {
top?: { value: number },
right?: { value: number }
},
color?: {
red?: number,
blue?: number
}
};
const res : Res = {};
const items = ["p-top-1", "p-right-2", 'c-red-300'];
for (let i = 0; i < items.length; i++) {
const item = items[i];
const [type, key, value] = item.split('-');
if(type === 'p') {
set(res, `position.${key}.value`, value);
}
if(type === 'c') {
set(res, `color.${key}.value`, value);
}
}
但是它没有类型安全,例如这不会出错:
if(type === 'p') {
set(res, `whatever-key-here.${key}.value`, value);
}
在
set
中有类似的功能moderndash
,但它缺少类型安全的set-existing-value
对应物
(它的工作原理与
lodash
的set
完全一样,所以你可以自由地import { set as setUntyped } from "lodash"
代替)
通过将其类型修改为 a-la-
set
而不是 a-la-assign
,如何实现 set
的类型安全版本就很简单了:
import { set as setUntyped } from "moderndash";
import type { Call, Objects, Strings } from "hotscript";
import type { PlainObject } from "moderndash";
export function set<TObj extends PlainObject, TPath extends Call<Objects.AllPaths, TObj>>(
obj: TObj, path: TPath, value: Call<Objects.Get<TPath>, TObj>
): TObj {
return setUntyped(obj, path, value) as TObj
}
要将它与拆分的字符串一起使用,您还需要一个
string.split
重载:
declare global {
interface String {
// typesafe 's-t-r-i-n-g'.split('-'): ['s', 't', 'r', 'i', 'n', 'g']
split<S extends string, D extends string>(this: S, separator: D): Call<Strings.Split<D>, S>;
}
}
然后示例代码显示错误,正如预期的那样: 游乐场:https://tsplay.dev/Nn886W
const res: Res = {};
const items = ["p-top-1", "p-right-2", 'c-red-300'] as const;
for (let i = 0; i < items.length; i++) {
const item = items[i];
const [type, key, value] = item.split('-')
if (type === 'p') {
set(res, `position.${key}.value`, value);
// ^!
// Argument of type '"1" | "2"' is not assignable to parameter of type 'number | undefined'.
}
if (type === 'c') {
set(res, `color.${key}.value`, value);
// ^!
// Argument of type '"300"' is not assignable to parameter of type 'undefined'.(2345)
}
}