如何用 self 对象(字面意思)推断方法参数

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

如何确保当我用

components: [1, 2]
注释掉第37行时,
available: (c) => {
中的参数c不是
never
而是获取类型
number

主要问题是,如果我注释掉这一行,所有其他条目,即使它们具有不同的

components:[...]
,也会变成
(c:never)=>{}

如果未提供

c
属性,我还希望方法中的参数
number
回退到类型
never
而不是
components

enum AAA {
    a1 = 'a1', a2 = 'a2', a3 = 'a3',
}
enum BBB {
    b1 = 'b1', b2 = 'b2', b3 = 'b3',
}


type TestData<T extends number = number> = {
    components?: T[];
    avaible: (c: T) => void; 
}

// Adjust the RR type to allow partial records for both AAA and BBB
type RR = Partial<Record<AAA, Partial<Record<BBB, number>>>>;


type Param<T extends RR> = {
    [KA in keyof T & AAA]?: {
        [KB in keyof T[KA] & BBB]?: TestData<Extract<T[KA][KB], number>>
    }
}

class Test<T extends RR> {
    constructor(
        public readonly data: Param<T>
    ) { }
}


// this is a db with literal entries
// i want pass to (c) type from .components or if not defined, juste use number
new Test(
    {
        [AAA.a1]: {
            [BBB.b1]: {
                components: [1, 2], // if i remove this, c should number and not never !?
                avaible: (c) => {
                    //    ^?
                }
            },
        },
        [AAA.a2]: {
            [BBB.b2]: {
                components: [5],
                avaible: (c) => {
                   //    ^?
                }
            },
              [BBB.b1]: {
                components: [8],
                avaible: (c) => {
                   //    ^?
                }
            },
        },
    }
);

游乐场

typescript typescript-generics
1个回答
0
投票

看起来主要问题是,当

T
constrained
RR
时,一旦你注释掉该行,推理就会失败,然后
T
回落到
RR
,此时你得到
Param<RR>
相当于
{[K in AAA]?: {}}
然后一切都停止工作。 我不是 100% 确定为什么会发生这种情况,但类型推断很棘手。

如果你移除约束,那么事情就会开始发生变化:

type Param<T> = {
    [KA in keyof T]: {
        [KB in keyof T[KA]]: (
            TestData<Extract<T[KA][KB], number>>
        )
    }
}

class Test<T> {
    constructor(
        public readonly data: Param<T>
    ) { }
}

new Test(
    {
        [AAA.a1]: {
            [BBB.b1]: {
                avaible: (c) => {
                    //    ^? (parameter) c: never
                }
            },
        },
        [AAA.a2]: {
            [BBB.b2]: {
                components: [5],
                avaible: (c) => {
                    //    ^? (parameter) c: 5
                }
            },
            [BBB.b1]: {
                components: [8],
                avaible: (c) => {
                    //    ^? (parameter) c: 8
                }
            },
        },
    }
);

现在仅推断出缺失的

components
never
。如果您希望它回落到
number
(这不是必需的,如果您愿意,您总是可以 注释
(c: number) => {}
),那么您可以使用额外的 条件类型:

type OrElse<T, D> = [T] extends [never] ? D : T;

type Param<T> = {
    [KA in keyof T]: {
        [KB in keyof T[KA]]: (
            TestData<OrElse<Extract<T[KA][KB], number>, number>>
        )
    }
}

new Test(
    {
        [AAA.a1]: {
            [BBB.b1]: {
                avaible: (c) => {
                    //    ^? (parameter) c: number
                }
            },
        },
        [AAA.a2]: {
            [BBB.b2]: {
                components: [5],
                avaible: (c) => {
                    //    ^? (parameter) c: 5
                }
            },
            [BBB.b1]: {
                components: [8],
                avaible: (c) => {
                    //    ^? (parameter) c: 8
                }
            },
        },
    }
);

看起来不错!


此时唯一的问题是

T
可以有各种键,而不仅仅是
AAA
BBB

 new Test({x: {}}); // no error?!

您可以通过多种方式添加回约束,可能是将具有意外键的属性映射到

never
,例如:

type Param<T> = {
    [KA in keyof T]: {
        [KB in keyof T[KA]]: (
            TestData<OrElse<Extract<T[KA][KB], number>, number>>
        ) & (KB extends BBB ? unknown : never)
    } & (KA extends AAA ? unknown : never)
}

这会留下好的案例,但会给你带来错误

new Test({ x: {} }); // error!

我会说防止意外键/值的确切方法超出了这里的范围,要点是删除约束

extends RR
并在
Extract<T[KA][KB], number>
上使用条件类型以导致
never
成为
number

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.