英文:
Typescript merge deep function without type 'any'
问题
如何将此答案转换为 TypeScript 而不使用类型 'any'?
目前(不起作用)尝试:
export function isObject(item: object): boolean {
return !!item && typeof item === "object" && !Array.isArray(item);
}
export function mergeDeep(target: object, ...sources: object[]): object {
if (!sources?.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
const sk = source[key];
if (isObject(sk)) {
if (!target[key]) Object.assign(target, { [key]: {} });
mergeDeep(target[key], sk);
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return mergeDeep(target, ...sources);
}
const obj1 = {
one: {
two: {
keep: "yes!",
change: "should not see me",
},
three: {
four: {
keep: "yes!",
change: "should not see me",
},
},
},
};
const obj2 = {
one: {
two: {
change: "NICE 1!!",
},
three: {
four: {
change: "NICE 2!!",
},
},
},
};
console.log(JSON.stringify(mergeDeep(obj1, obj2), null, 2));
英文:
How to convert [this answer][1] to typescript without using the type 'any'?
Currently (not working) try:
export function isObject(item: object): boolean {
return !!item && typeof item === "object" && !Array.isArray(item);
}
export function mergeDeep(target: object, ...sources: object[]): object {
if (!sources?.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
const sk = source[key];
if (isObject(sk)) {
if (!target[key]) Object.assign(target, { [key]: {} });
mergeDeep(target[key], sk);
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return mergeDeep(target, ...sources);
}
const obj1 = {
one: {
two: {
keep: "yes!",
change: "should not see me",
},
three: {
four: {
keep: "yes!",
change: "should not see me",
},
},
},
};
const obj2 = {
one: {
two: {
change: "NICE 1!!",
},
three: {
four: {
change: "NICE 2!!",
},
},
},
};
console.log(JSON.stringify(mergeDeep(obj1, obj2), null, 2));
答案1
得分: 1
如果您并不真的关心使用非常强类型,只是想让事情编译而不使用不安全的 any
类型,您可以使用更安全的 unknown
类型。对于 isObject()
,您可以将其标记为自定义类型保护函数,以便结果将缩小输入到允许访问任何属性键的对象类型:
function isObject(item: unknown): item is Record<string, unknown> {
return !!item && typeof item === "object" && !Array.isArray(item);
}
function mergeDeep(target: unknown, ...sources: unknown[]): unknown {
if (!sources?.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
const sk = source[key];
if (isObject(sk)) {
let tk = target[key];
if (!tk) tk = target[key] = {};
mergeDeep(tk, sk);
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return mergeDeep(target, ...sources);
}
这基本上是相同的内容,但我将 target[key]
更改为保存的 tk
变量,因为编译器无法轻松确定如果键是任意的 string
,则对索引访问的结果会发生什么。有关此问题的相关开放问题,请参见 microsoft/TypeScript#10530。
请注意,当完成时,输出只是 unknown
类型。您可能希望输出类型与输入更密切地关联,以便,例如,mergeDeep({a: 123}).a
已知存在且类型为 number
。但这超出了问题的范围。
英文:
If you don't really care about using very strong types and you just want to get things to compile without the unsafe any
type, you can use the safer unknown
type. And for isObject()
you can mark it as a custom type guard function, so that the result will narrow the input to an object type which allows any property key to be accessed:
function isObject(item: unknown): item is Record<string, unknown> {
return !!item && typeof item === "object" && !Array.isArray(item);
}
function mergeDeep(target: unknown, ...sources: unknown[]): unknown {
if (!sources?.length) return target;
const source = sources.shift();
if (isObject(target) && isObject(source)) {
for (const key in source) {
const sk = source[key];
if (isObject(sk)) {
let tk = target[key];
if (!tk) tk = target[key] = {};
mergeDeep(tk, sk);
} else {
Object.assign(target, { [key]: source[key] });
}
}
}
return mergeDeep(target, ...sources);
}
That's pretty much the same thing but I changed target[key]
to a saved tk
variable because the compiler can't easily tell what happens to an indexed access like that if the key is an arbitrary string
. See microsoft/TypeScript#10530 for the relevant open issue about that.
Note that when you're done, the output is just of type unknown
. You might want the output type to be something more closely tied to the input, so that, for example, mergeDeep({a: 123}).a
is known to exist and be of type number
. But that's out of scope for the question as asked.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论