0

我有通用组件(具有以下类型):

const Search = <T extends object | number | string, K extends T extends object ? keyof T : T>(props: SearchProps<T,K>) => {
    const { data, searchKey } = props;

    return (
      <div> Test </div>
    )
};

export type SearchProps<T, K> = {
    type: SearchType;
    setSearcheData: Function;
    data: T[];
    searchKey: K;
};

您应该能够像在示例中一样使用它:

const dataObject: dataObjectType = [
{
    name: '1',
    id: 1,
},
{
    name: '2',
    id: 2,
}
];

const dataString = ['1', '2'];

<Search
        data={dataString}
        searchKey={'string'}
        type={SearchType.SingleIcon}
        setSearcheData={()=>{}}
    />

我应该能够data作为 Array 传递,并且searchKey好像 T 是对象、它的键或只是一个类型。

但是当我尝试做类似的事情时

data[0][`${searchKey}`]

我收到了一个 TS 错误。

我尝试了类似的东西

typeof data[0] === 'object' or Object.keys(data[0]).find(key => key === `${searchKey}`)

但它不起作用。

有任何想法吗?

4

1 回答 1

1

我认为问题发生在Search().

这里有许多问题阻止编译器验证您正在做的事情是类型安全的。最后,您很可能需要放弃这种自动类型安全验证,并通过使用类型断言告诉编译器不要担心它。

以下是我看到的问题:

  • 在 的实现内部Search(), 的类型props取决于未指定的泛型类型参数TK; 编译器可以很好地推断指定T类型的类型安全性,但是当它具有像and这样的未指定泛型时K,它就不那么聪明了。具体来说,类型保护之类的控制流分析不会影响泛型类型参数(有关详细信息,请参阅microsoft/TypeScript#24085)。因此,您可以根据需要进行props尽可能多的测试,并且它不会让编译器理解T应该被认为可以分配给object(而不是object | string | number)或K应该被认为可以分配给keyof T.

  • 在 的 实现 中Search()的 泛型 约束KT extends object ? keyof T : T一个 条件 类型 , 它 本身 依赖 于 一个 未 指定 的 泛型 类型 参数T. 编译器在处理这种“未解析的条件类型”方面尤其糟糕。(有很多 GitHub 问题,这是根本问题;例如,您可以查看microsoft/TypeScript#35257。)我的意思是,即使这样也行不通:

     function foo<T>(t: T) {
         const x: number extends T ? T : T = t; // error!
         // Type 'T' is not assignable to type 'number extends T ? T : T'
     }
    

如果编译器无法验证number extends T ? T : T本质上与 相同T,那么它就无法处理这里更复杂的情况。

  • 您已将 的属性props分解为两个单独的变量datasearchKey. 一旦你这样做了,编译器就完全忘记了它们相互关联的事实。(有关更多信息,请参阅microsoft/TypeScript#30581)这意味着当您检查时,例如typeof data[0] === "object",编译器没有意识到它对searchKey. 它将datasearchKey视为完全独立的变量。

  • 为了确定K是 akeyof T还是 a T,您需要检查data[0]orprops.data[0]是否属于objectstringnumber。这些类型不是“单例类型”,因此编译器不会将props其视为可区分的 union,即使您可以将其类型编写为SearchProps<T, keyof T> | SearchProps<string | number, string | number>. 因此,在检查可区分联合的属性时,没有很好的方法可以利用联合过滤。

所有这些放在一起意味着编译器实际上没有能力为您验证类型安全。在这种情况下,您比编译器了解更多,您可能应该使用类型断言:

const Search = <T extends object | number | string, K extends T extends object ? keyof T : T>(
    props: SearchProps<T, K>
) => {
    const { data, searchKey } = props;
    if (typeof data[0] === 'object') {
        data[0][searchKey as keyof T]
    } else {
        data.indexOf(searchKey as T);
    }
};

在上面,searchKey as keyof TsearchKey as T类型断言,你告诉编译器你知道什么searchKey,它只是相信你并继续前进。那应该抑制错误。请注意,这会给您带来验证类型安全的负担,并且您应该认真承担此责任,因为如果您进行了一些使您的假设无效的更改,您不能期望出现警告。例如,您可以更改(typeof data[0] === 'object')(typeof data[0] !== 'object'),编译器不会注意到。所以要小心。


好的,希望有帮助;祝你好运!

Playground 代码链接

于 2020-04-06T14:03:46.780 回答