Доступ к обобщенным c данным через переданный ключ - PullRequest
0 голосов
/ 06 апреля 2020

У меня есть универсальный c компонент (с типами ниже):

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;
};

U должен иметь возможность использовать его, как в примере:

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}`)

Но это не работает.

Есть идеи?

1 Ответ

1 голос
/ 06 апреля 2020

Я предполагаю, что проблема возникает в реализации Search().

. Здесь есть ряд проблем, которые мешают компилятору проверить, что то, что вы делаете, является типобезопасным. В конце вам, скорее всего, придется отказаться от такой автоматической проверки безопасности типа c и сказать компилятору не беспокоиться об этом, используя утверждение типа .

Здесь Вот проблемы, которые я вижу:

  • Внутри реализации Search() тип props зависит от неопределенных generi c параметров типа T и K; компилятор может достаточно хорошо рассуждать о безопасности типов для указанных типов, но когда он имеет неуказанные обобщенные значения, такие как T и K, он не такой умный. В частности, анализ потока управления , такой как защита типов, не влияет на параметры типа c (для получения дополнительной информации см. microsoft / TypeScript # 13995 ). Таким образом, вы можете тестировать props столько, сколько хотите, и это не даст компилятору понять, что T следует считать присваиваемым object (в отличие от object | string | number) или K присваивается keyof T.

  • Внутри реализации Search() общее ограничение c для K равно T extends object ? keyof T : T, условный тип, который сам по себе зависит от неопределенного универсальный c параметр типа 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 на две отдельные переменные data и searchKey. После этого компилятор полностью теряет тот факт, что они коррелируют друг с другом. (См. microsoft / TypeScript # 30581 для получения дополнительной информации.) Это означает, что при проверке, скажем, typeof data[0] === "object", компилятор не понимает, что он имеет какое-либо влияние на тип searchKey. Он обрабатывает data и searchKey как полностью независимые переменные.

  • Чтобы определить, является ли K keyof T или T, необходимо проверить, data[0] или props.data[0] относится к типу object, string или number. Эти типы не являются «одноэлементными типами», и поэтому компилятор не будет обрабатывать props как различимое объединение , даже если вы можете написать его тип как 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 T и searchKey as T - это утверждения типа, в которых вы сообщаете компилятору что вы знаете о searchKey, и он просто верит вам и движется дальше. Это должно подавить ошибки. Обратите внимание, что это накладывает на вас бремя проверки безопасности типов, и вы должны серьезно относиться к этой ответственности, поскольку вы не можете ожидать появления предупреждения, если внесете какое-либо изменение, которое сделает ваши предположения недействительными. Например, вы можете изменить (typeof data[0] === 'object') на (typeof data[0] !== 'object'), и компилятор не заметит. Так что будьте осторожны.


Хорошо, надеюсь, это поможет; удачи!

Детская площадка ссылка на код

...