gpt4 book ai didi

typescript - 是否可以在 TypeScript 中递归推断泛型类型?

转载 作者:行者123 更新时间:2023-12-03 14:47:22 27 4
gpt4 key购买 nike

以这段代码为例:

const [divEl, spanEl] = createElements([
['div', { id: 'test' }, [
['a', { href: 'test' }, [
['img', { src: 'test' }, null]
]],
['img', { href: 'test' }, null]
]],
['span', null, null]
]);
我想让 TypeScript 推断 divEl 的类型成为 HTMLDivElementspanEl 的类型成为 HTMLSpanElement .而且我还希望它检查给定的属性并根据推断的类型显示错误(例如,它应该在 ['img', { href: 'test' }, null] 上显示错误,因为 HTMLImageElement 没有 href 属性)。
经过一番研究,这就是我迄今为止所拥有的:
type ElementTag = keyof HTMLElementTagNameMap;

type ElementAttributes<T extends ElementTag> = {
[K in keyof HTMLElementTagNameMap[T]]?: Partial<HTMLElementTagNameMap[T][K]> | null;
};

type ElementArray<T extends ElementTag> = [
T,
ElementAttributes<T> | null,
ElementArray<ElementTag>[] | string | null
];

type MappedElementArray<T> = {
[K in keyof T]: T[K] extends ElementArray<infer L> ? HTMLElementTagNameMap[L] : never;
};

// This is the signature for the createElements function.
type CreateElements = <T extends ElementArray<ElementTag>[] | []>(
array: T
) => MappedElementArray<T>;
我得换 ElementArray<ElementTag>[]到一些通用的东西,但我不知道如何进行。
这甚至可能吗?
我知道可以手动完成,但是 T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, ...不漂亮。

最佳答案

(部分答案)
TypeScript 不喜欢递归类型。你可以解决这个问题,只要你不需要推理......但是,由于这里需要,我认为不可能让 TS 采取最后一步。
您可以拥有以下功能之一:

  • 将返回类型推断为具有正确返回类型的元组。
  • 对传入的数组进行深度类型检查

  • 第一个很容易。我们只需要 TcreateElements扩大受歧视的联盟。你已经有了这个,但这是一种稍微不同的看待它的方式。
    type DiscriminatedElements = {
    // you can, of course, do better than unknown here.
    [K in keyof HTMLElementTagNameMap]: readonly [K, Partial<HTMLElementTagNameMap[K]> | null, unknown]
    }[keyof HTMLElementTagNameMap]

    type ToElementTypes<T extends readonly DiscriminatedElements[]> = {
    [K in keyof T]: T[K] extends [keyof HTMLElementTagNameMap, any, any] ? HTMLElementTagNameMap[T[K][0]] : never
    }

    declare const createElements: <T extends readonly DiscriminatedElements[] | []>(
    array: T
    ) => ToElementTypes<T>

    createElements([
    ['span', {}, null]
    ]) // [HTMLSpanElement]
    第二个有点棘手。正如我之前所说,TS 不喜欢递归类型。见 GH#26980 .也就是说,我们可以绕过它来创建一种任意深度的类型来进行检查……但是如果我们尝试将这种类型与任何推理结合起来,TS 将意识到它可能是无限的。
    type DiscriminatedElements = {
    [K in keyof HTMLElementTagNameMap]: readonly [K, Partial<HTMLElementTagNameMap[K]> | null]
    }[keyof HTMLElementTagNameMap]

    type NumberLine = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    type CreateElementArray<T extends readonly [any, any], N extends number> = T extends readonly [infer A, infer B] ? {
    done: readonly [A, B, null],
    recurse: readonly [A, B, null | readonly CreateElementArray<DiscriminatedElements, NumberLine[N]>[]]
    }[N extends 0 ? 'done' : 'recurse'] : never

    // Increase up N and NumberLine as required
    type ElementItem = CreateElementArray<DiscriminatedElements, 4>

    declare const createElements: (
    array: readonly ElementItem[]
    ) => HTMLElement[];


    const [divEl, spanEl] = createElements([
    ['div', { id: 'test' }, [
    ['a', { href: 'test' }, [
    ['img', { src: 'test' }, null]
    ]],
    ['img', { href: 'test' }, null] // error, as expected
    ]],
    ['span', null, null]
    ]);
    我认为可变参数元组在这里没有帮助。它们将是该语言的一个很棒的补充,但不能解决您在此处尝试建模的问题。
    让您保持两全其美的解决方案是接受 HTML 元素作为元组中的第三项,只需调用 createElements在那个阵列中。

    关于typescript - 是否可以在 TypeScript 中递归推断泛型类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62564706/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com