TypeScript:具有可变元组类型的依赖类型推断

问题描述

更新:对于所需的行为,TypeScript 需要存在的泛型类型 - 并且好像 TS 4.1 没有它们。感谢有帮助的回答。我认为要解决键入 react-query useQueries 的问题,我们仍然可以在提供 unkNown 时使用 selector。我会努力让它发挥作用,看看它会去哪里。

考虑以下事项:

interface Data<TData = unkNown,TSelected = unkNown> {
    data: TData;
    selector?: (data: TData) => TSelected
}

function makeArrayAsConstItemsForDataTypesOnly<
    TItem extends readonly Data[]
>(arr: [...TItem]): { [K in keyof TItem]: { item: Extract<TItem[K],Data>["data"] } } {
    return arr.map(item => {
        return item.selector 
            ? { item: item.selector(item.data) }
            : { item: item.data }
    }) as any;
}

const returnedData = makeArrayAsConstItemsForDataTypesOnly([
    { data: { nested: 'thing' },selector: d => d.nested },{ data: 1 },{ data: 'two' }])

returnedData 采用以下类型:

const returnedData: [{
    item: {
        nested: string;
    };
},{
    item: number;
},{
    item: string;
}]

selector 可能会或可能不会随每个元素一起提供。如果提供,它会映射提供的 data 类型并转换返回的数据。

鉴于上面的例子,理想情况下返回的类型是:

const returnedData: [{
    item: string;
},{
    item: string;
}]

唉,在 selector: d => d.nested 中,d 也不是 unkNown 类型,而不是 TData 类型。所以我们并没有像希望的那样进行类型推断。

返回类型的伪代码如下所示:

  • 对于数组的每个条目:
    • 获取 data 属性
    • 如果数组条目包含 selector,则返回 { item: entry.selector(entry.data) }
    • else 返回 { item: entry.data }

是否可以通过 TypeScript 中的类型系统来表达这一点? See playground here.

所以这里有两个问题:

  • selector 流经 TData 作为输入
  • 整体函数的返回类型

解决方法

// credits goes to https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type/50375286#50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
  k: infer I
) => void
  ? I
  : never;

//credits goes to https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468114901
type UnionToOvlds<U> = UnionToIntersection<
  U extends any ? (f: U) => void : never
>;

//credits goes to https://github.com/microsoft/TypeScript/issues/13298#issuecomment-468114901
type PopUnion<U> = UnionToOvlds<U> extends (a: infer A) => void ? A : never;

//credits goes tohttps://stackoverflow.com/questions/53953814/typescript-check-if-a-type-is-a-union#comment-94748994
type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;

type UnionToArray<T,A extends unknown[] = []> = IsUnion<T> extends true
  ? UnionToArray<Exclude<T,PopUnion<T>>,[PopUnion<T>,...A]>
  : [T,...A];

type Values<T> = T[keyof T]
type MapPredicate<T> = { item: Values<T> };

// http://catchts.com/tuples
type MapArray<
  Arr extends ReadonlyArray<unknown>,Result extends unknown[] = []
  > = Arr extends []
  ? Result
  : Arr extends [infer H]
  ? [...Result,MapPredicate<H>]
  : Arr extends readonly [infer H,...infer Tail]
  ? MapArray<Tail,[...Result,MapPredicate<H>]>
  : never;

type Test1 = MapArray<[{nested:42},{a:'hello'}]>[0] // { item: 42; }

interface Data<TData = any,TSelected = any> {
  data: TData;
  selector?: (data: TData) => TSelected
}

const builder = <T,R>(data: T,selector?: (data: T) => R): Data<T,R> => ({
  data,selector
})

type Mapper<T extends Data> = T['selector'] extends (...args: any[]) => any ? ReturnType<T['selector']> : T['data']

const first = builder({ nested: 'thing' },d => d.nested);
const second = builder({ a: 42 });

type First = typeof first
type Second = typeof second

type Result = Mapper<First>

const arr = [first,second];

function makeArrayAsConstItemsForDataTypesOnly<T extends Data>(data: Array<T>) {
  const result = data.map((item) => {
    return item.selector
      ? { item: item.selector(item.data) }
      : { item: item.data }
  })

  /**
   * I don't know how to avoid type casting here
   * I tried different approaches,but none of them
   * helped
   */
  return result as MapArray<UnionToArray<Mapper<T>>>
}

const test = makeArrayAsConstItemsForDataTypesOnly(arr)

type ResultArray = typeof test;

type FirstElement = ResultArray[0] // { item: string }
type SecondElement = ResultArray[1] // { item: number }

我知道,使用类型转换不是最好的解决方案,但我无法以更好的方式推断泛型。

This answer 可能会帮助您以更好的类型安全方式构建带有回调的数据结构

这些链接可能会帮助您了解这里发生了什么:

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...