我的代码如下所示:
type Getter<T> = () => T;
type GetterOrStatic<T> = T | Getter<T>;
type Input<T> = T extends {[K: string ]: string} ? { [K in keyof T]: GetterOrStatic<T[K]>} : T;
declare function f<T extends {[K: string ]: string} | string>(value: Input<T>): T;
const x = f({ x : 'x'}); // <-- correctly inferred as `{ x: string }`
const y = f({y: (): string => 'y'}); // <-- I expect it to be `{ y: string }` but it is `string | { [K: string]: string }`
当然,我希望
y
元素被推断为 { y: string }
。
我尝试过的一件事是像这样使用重载:
declare function f<T extends {[K: string ]: string}>(value: { [K in keyof T]: GetterOrStatic<T[K]> }): T;
declare function f<T extends {[K: string ]: string} | string>(value: Input<T>): T;
但是,这会导致第一个示例 (
x
) 被推断为 { x: 'x' }
而不是 {x: string}
。我猜测是因为基于 TypeScript,我希望进一步缩小类型,因为现在条件只有第一个溢出选项中的 {[K: string ]: string}
。
我发现我可以通过这样做来解决这个问题(以相当“hacky”的方式):
declare function f<T extends {[K: string ]: string} | {}>(value: { [K in keyof T]: GetterOrStatic<T[K]> }): T;
declare function f<T extends {[K: string ]: string} | string>(value: Input<T>): T;
现在这个示例可以运行,但在类似但更细微的用例中还存在其他问题:
GetterOrStatic
(或我在那里使用的其他任何东西)只能接受字符串),则T[K]
上存在类型错误。 游乐场extend
条件来推断数组的类型,这让我们回到了最初的问题。 游乐场顺便说一句,这两个问题都与我的实际用例相关(这太复杂,无法在此处发布,因此为了本文的目的,我尝试进行简化)。
我怎样才能在这里完成我想要的事情?
一个有趣的小谜题,这就是我所知道的。我最终完全放弃了
Input
类型(在我看来,类型联合方法使解决方案过于复杂)并使用重载,尽管我可能不完全理解您的需求范围,但它似乎有效。
type Getter<T> = () => T;
type GetterOrStatic<T> = T | Getter<T>;
type GetterOrStaticRecord<T extends Record<string, string>> = { [K in keyof T]: GetterOrStatic<T[K]> };
declare function f<T extends string>(value: GetterOrStatic<T>): T;
declare function f<T extends Record<string, string>>(value: GetterOrStaticRecord<T>): T;
const x = f({ x: 'x' as string }); // <-- inferred as `{ x: string }`
const y = f({ y: (): string => 'y' }); // <-- inferred as `{ y: string }`
const z = f('z' as string); // <-- inferred as `string`
const w = f((): string => 'w' as string); // <-- inferred as `string`
// ================================================================================================
type InputData = { a: string, b: string, c: string };
type InputType = GetterOrStaticRecord<InputData>;
const input: InputType = {
a: 'a',
b: 'b',
c: () => 'c'
};
const output = f(input); // inferred as 'InputData'
const { a, b, c } = output; // all inferred as 'string'