如何消除部分索引类型中未定义的联合?
How do I eliminate union with undefined in partial index type?
我有一个 objectMap()
函数,它接受一个对象,对每个值应用一个函数,return 是一个具有相同键但不同值的对象。我为此写了以下声明:
declare function objectMap<TInputObject, TOutputValue>(
target: TInputObject,
callback: (currentValue: TInputObject[keyof TInputObject]) => TOutputValue,
): {[k in keyof TInputObject]: TOutputValue};
我希望这会使用在对象值中找到的类型调用回调函数,并且 return 一个与输入对象具有相同键结构的对象。这适用于大多数类型的对象,例如简单映射甚至字符串文字的并集:
type in1 = {[k: string]: string}
type keys = 'a' | 'b' | 'c'
type in2 = {[k in keys]: number}
但它分解为字符串文字联合类型的可选键:
type keys = 'a' | 'b' | 'c'
type in = {[key in keys]?: number }
在这种情况下,当我尝试使用函数时:
const obj : {[key in keys]?: number} = {a: 1, b: 2}
const result = objectMap(obj, x => x+1)
变量x
获得类型number | undefined
(即使所有存在的键都有一个实际的数值),并且result
除了可选键。
如何更改 objectMap()
的声明以获得预期结果,其中 x
只是 number
而 result
是 {[k in keys]?: number
?
我想这取决于 objectMap()
的实施;假设对于 Object.keys(target)
中的每个键 k
,您首先要确保 target[k]
不是 undefined
(如果 k
是可选键,则可能发生这种情况)在调用 callback
之前。如果是这样,那么您可以稍微修改 objectMap()
的签名,以便 callback
参数不需要担心 undefined
参数,通过使用 predefined conditional type Exclude<U, X>
... 它采用联合类型 U
并删除可分配给 X
的任何成分,如下所示:
function objectMap<I, O>(
target: I,
callback: (currentValue: Exclude<I[keyof I], undefined>) => O
) {
const ret = {} as { [K in keyof I]: O };
let k: keyof I;
for (k in target) {
const tk = target[k];
if (typeof tk !== "undefined") {
ret[k] = callback(tk as Exclude<I[keyof I], undefined>);
}
}
return ret;
}
鉴于该签名,您现在可以像这样使用 objectMap()
:
interface A {
a?: number;
b: number;
c?: number;
}
const a: A = { a: 1, b: 2 };
const result = objectMap(a, x => x.toFixed());
// const result: {a?: string | undefined, b: string, c?: string | undefined }
console.log(result); // {a: "1", b: "2"}
因此在这种情况下,A
接口具有可选的数字属性 "a"
和 "c"
,以及必需的数字 属性 "b"
。回调的类型为(x: number)=>string
,返回的结果类型具有相同的可选和必需的属性键,但值是string
类型。 (请注意,{a?: string | undefined, b: string, c?: string | undefined}
类型与 {a?: string, b: string, c?: string}
类型相同)如果编译器在编译时只知道 a
值是可赋值的,那么这就是编译器所能做的最好的事情至 A
.
好的,希望对您有所帮助;祝你好运!
我有一个 objectMap()
函数,它接受一个对象,对每个值应用一个函数,return 是一个具有相同键但不同值的对象。我为此写了以下声明:
declare function objectMap<TInputObject, TOutputValue>(
target: TInputObject,
callback: (currentValue: TInputObject[keyof TInputObject]) => TOutputValue,
): {[k in keyof TInputObject]: TOutputValue};
我希望这会使用在对象值中找到的类型调用回调函数,并且 return 一个与输入对象具有相同键结构的对象。这适用于大多数类型的对象,例如简单映射甚至字符串文字的并集:
type in1 = {[k: string]: string}
type keys = 'a' | 'b' | 'c'
type in2 = {[k in keys]: number}
但它分解为字符串文字联合类型的可选键:
type keys = 'a' | 'b' | 'c'
type in = {[key in keys]?: number }
在这种情况下,当我尝试使用函数时:
const obj : {[key in keys]?: number} = {a: 1, b: 2}
const result = objectMap(obj, x => x+1)
变量x
获得类型number | undefined
(即使所有存在的键都有一个实际的数值),并且result
除了可选键。
如何更改 objectMap()
的声明以获得预期结果,其中 x
只是 number
而 result
是 {[k in keys]?: number
?
我想这取决于 objectMap()
的实施;假设对于 Object.keys(target)
中的每个键 k
,您首先要确保 target[k]
不是 undefined
(如果 k
是可选键,则可能发生这种情况)在调用 callback
之前。如果是这样,那么您可以稍微修改 objectMap()
的签名,以便 callback
参数不需要担心 undefined
参数,通过使用 predefined conditional type Exclude<U, X>
... 它采用联合类型 U
并删除可分配给 X
的任何成分,如下所示:
function objectMap<I, O>(
target: I,
callback: (currentValue: Exclude<I[keyof I], undefined>) => O
) {
const ret = {} as { [K in keyof I]: O };
let k: keyof I;
for (k in target) {
const tk = target[k];
if (typeof tk !== "undefined") {
ret[k] = callback(tk as Exclude<I[keyof I], undefined>);
}
}
return ret;
}
鉴于该签名,您现在可以像这样使用 objectMap()
:
interface A {
a?: number;
b: number;
c?: number;
}
const a: A = { a: 1, b: 2 };
const result = objectMap(a, x => x.toFixed());
// const result: {a?: string | undefined, b: string, c?: string | undefined }
console.log(result); // {a: "1", b: "2"}
因此在这种情况下,A
接口具有可选的数字属性 "a"
和 "c"
,以及必需的数字 属性 "b"
。回调的类型为(x: number)=>string
,返回的结果类型具有相同的可选和必需的属性键,但值是string
类型。 (请注意,{a?: string | undefined, b: string, c?: string | undefined}
类型与 {a?: string, b: string, c?: string}
类型相同)如果编译器在编译时只知道 a
值是可赋值的,那么这就是编译器所能做的最好的事情至 A
.
好的,希望对您有所帮助;祝你好运!