【问题标题】:Typescript Generics return type based on argument基于参数的 Typescript 泛型返回类型
【发布时间】:2023-03-30 11:55:01
【问题描述】:

如何根据参数进行动态打字稿返回类型未定义或未定义。请帮忙。

还添加了Typescript Playground Snippet供参考

我想出了这个,但没有按预期工作。

V extends keyof T ? ToObject<T> : ToObject<T[keyof T]>

代码:

export interface ToObject<T> {
  [k: string]: T;
}

export const toObject = <T, V = (keyof T)>(
  list: T[],
  key: keyof T,
  valueKey?: V
): V extends keyof T ? ToObject<T> : ToObject<T[keyof T]> => valueKey === undefined
    ? list.reduce((all, cur) => ({
      ...all,
      [cur[key] as unknown as string]: cur
    }), {} as ToObject<T>)
    : list.reduce((all, cur) => ({
      ...all,
      [cur[key] as unknown as string]: cur[valueKey as unknown as keyof T]
    }), {} as ToObject<T[keyof T]>);

用法:

interface Data{
  a: string;
  b: string;
  c: string;
}
const list: Data[] = [
  {a:'a', b: 'b', c: 'c'},
  {a:'1', b: '2', c: '3'}
];

// use 1
toObject<Data>(list, 'a');
// response
{
  {'a': {a:'a', b: 'b', c:'c'}},
  {'1': {a:'1', b:'2', c: '2'}},
}

// Use 2
toObject<Data>(list, 'a', 'b');
//response
{ {'a': 'b'}, {'1': '2'} }

问题:

// use 1 ERROR -> Cant do this
const dataMap: ToObject<Data> = toObject<Data>(list, 'a');
// Type 'ToObject<Data> | ToObject<string>' is not assignable to type 'ToObject<Data>'.
// Type 'ToObject<string>' is not assignable to type 'ToObject<Data>'.
// Type 'string' is not assignable to type 'Data'.(2322)

// Use 2 ERROR -> Cant do this
const dataKeyMap: ToObject<string> = toObject<Data>(list, 'a', 'b');
// Type 'ToObject<Data> | ToObject<string>' is not assignable to type 'ToObject<string>'.
// Type 'ToObject<Data>' is not assignable to type 'ToObject<string>'.
// Type 'Data' is not assignable to type 'string'.(2322)

【问题讨论】:

    标签: typescript typescript-generics


    【解决方案1】:

    正如所写,TS 没有足够的信息来区分toObject 的两种返回类型。

    如果我们添加function overloads,我们可以将返回类型绑定到函数签名。如果通过valueKey, toObject 返回ToObject&lt;T[keyof T]&gt;,如果不是,则函数返回ToObject&lt;T&gt;。

    Revised Playground

    export function toObject <T, V = (keyof T)>(list: T[], key: keyof T): ToObject<T>
    export function toObject <T, V = (keyof T)>(list: T[], key: keyof T, valueKey: V): ToObject<T[keyof T]>
    export function toObject <T, V = (keyof T)>(list: T[], key: keyof T, valueKey?: V): ToObject<T> | ToObject<T[keyof T]> {
      return valueKey === undefined
        ? list.reduce((all, cur) => ({
          ...all,
          [cur[key] as unknown as string]: cur
        }), {} as ToObject<T>)
        : list.reduce((all, cur) => ({
          ...all,
          [cur[key] as unknown as string]: cur[valueKey as unknown as keyof T]
        }), {} as ToObject<T[keyof T]>);
    }  
    

    除了让 TS 编译器满意之外,重载还告诉人类用户应该如何调用函数。

    【讨论】:

    • 好的,我如何将这些添加到export const toObject = () =&gt;{},因为我将此函数导入另一个 ts 文件
    • 简单,轻松。在三个 toObject 签名之前附加单词 export(参见上面的编辑)。这种函数重载风格不适用于箭头函数,所以我将toObject重构为函数声明。
    • @fedonev 箭头函数重载:export const toObject: { &lt;T, V = (keyof T)&gt;(list: T[], key: keyof T): ToObject&lt;T&gt; &lt;T, V = (keyof T)&gt;(list: T[], key: keyof T, valueKey: V): ToObject&lt;T[keyof T]&gt; } = &lt;T, V = (keyof T)&gt;(list: T[], key: keyof T, valueKey?: V): ToObject&lt;T&gt; | ToObject&lt;T[keyof T]&gt; =&gt; { ... }
    猜你喜欢
    • 1970-01-01
    • 2022-11-04
    • 2020-10-27
    • 1970-01-01
    • 2020-01-23
    • 2019-06-07
    • 1970-01-01
    • 2021-11-10
    • 1970-01-01
    相关资源
    最近更新 更多