通过传递的密钥访问通用数据

Access generic data via passed key

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

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 作为数组传递,并且 searchKey 就好像 T 是对象、它的键或只是一个类型一样。

但是当我尝试做类似

的事情时
data[0][`${searchKey}`]

我遇到了 TS 错误。

我试过

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

但是没用。

有什么想法吗?

我认为问题出在 Search() 的实现中。

这里有很多问题阻止编译器验证您正在做的事情是类型安全的。最后,您很可能需要放弃这种自动类型安全验证,并通过使用 type assertion.

告诉编译器不要担心它。

以下是我看到的问题:

  • Search()的实现中,props的类型取决于未指定泛型类型参数TK;编译器可以很好地推断 指定的 类型的类型安全性,但是当它具有未指定的泛型(如 TK 时,它就没有那么聪明了。具体来说,control flow analysis like type guarding, does not affect generic type parameters (see microsoft/TypeScript#24085 了解更多信息)。所以你可以尽可能多地测试 props,它不会让编译器理解 T 应该被认为是可分配给 object(而不是 object | string | number)或者 K 应该被认为可以分配给 keyof T.

  • Search()的实现中,K的泛型约束是T 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 : TT 本质上相同,那么它将无法处理这里更复杂的情况。

  • 您已将 props 的属性分解为两个单独的变量 datasearchKey。一旦这样做,编译器就会完全忘记它们彼此 相关 的事实。 (有关详细信息,请参阅 microsoft/TypeScript#30581)这意味着当您检查 typeof data[0] === "object" 时,编译器不会意识到它对 searchKey 的类型有任何影响。它将 datasearchKey 视为完全独立的变量。

  • 为了判断Kkeyof T还是T,需要检查data[0]还是props.data[0] 的类型为 objectstringnumber。这些类型不是“单一类型”,因此编译器不会将 props 视为 discriminated 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 link to code