一般推理问题

问题描述 投票:0回答:1

我的代码如下所示:

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;

现在这个示例可以运行,但在类似但更细微的用例中还存在其他问题:

  1. 如果
    GetterOrStatic
    (或我在那里使用的其他任何东西)只能接受字符串),则
    T[K]
    上存在类型错误。 游乐场
  2. 如果我使用数组而不是对象,这会迫使我对参数使用
    extend
    条件来推断数组的类型,这让我们回到了最初的问题。 游乐场

顺便说一句,这两个问题都与我的实际用例相关(这太复杂,无法在此处发布,因此为了本文的目的,我尝试进行简化)。

我怎样才能在这里完成我想要的事情?

typescript
1个回答
0
投票

一个有趣的小谜题,这就是我所知道的。我最终完全放弃了

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'

游乐场

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.