машинописное динамическое и безопасное добавление элемента в массив - PullRequest
2 голосов
/ 09 марта 2019

Я портирую приложение Javascript на Typescript.У меня есть объект со многими свойствами массива, содержащими данные, и функция JavaScript делает доступ к массивам данных.Для простоты я рассматриваю только метод add

function updateDataStructure(state, structureName, elem) {
   state.data[structureName].push(elem);
   return state.data[structureName];
}

Портирование на машинописный текст Я хотел бы ввести безопасность типов и поддерживать универсальный код.

interface AA {
    lep: string,
    snap: number,
    sul: boolean
}

interface BB {
    p1: string;
    val: number;
}

interface StateDataContainer {
    aa: AA[],
    bb: BB[],
}

class State {
    data: StateDataContainer;
}

export type DataContainerProps = keyof StateDataContainer;
type ArrayElement<ArrayType> = ArrayType extends (infer ElementType)[] ? ElementType : never;

function updateDataStructure<K extends DataContainerProps, D extends ArrayElement<StateDataContainer[K]>>
                (state: State, structureName: K, elem: D)
    : StateDataContainer[K] {

    // @ts-ignore
    state.data[structureName].push(elem);
    return state.data[structureName];
}

С этим кодом у меня есть типизированный сейфИнтерфейс, компилятор проверяет клиентский код, чтобы добавить правильные объекты в правильную структуру.

const state = new State();

let avv: BB;
let line: AA;

let v: BB[] = updateDataStructure(state, 'bb', avv!);
let l: AA[] = updateDataStructure(state, 'aa', line!);

с этим решением я должен добавить использовать аннотацию // @ ts-ignore в коде реализации, чтобы избежать компилятораошибки вроде:

 Argument of type 'D' is not assignable to parameter of type 'AA & BB'.
  Type 'ArrayElement<StateDataContainer[K]>' is not assignable to type 'AA & BB'.
    Type '{}' is not assignable to type 'AA & BB'.
      Type '{}' is not assignable to type 'AA'.
        Type 'AA | BB' is not assignable to type 'AA & BB'.

Есть ли лучший подход для написания этого кода?

Ответы [ 2 ]

0 голосов
/ 10 марта 2019

Я думаю, что другая проблема заключается в том, что TypeScript не понимает семантику вашего типа ArrayElement, то есть он не знает, что это означает что-то, что вы можете добавить к StateDataContainer[K].

В одну сторонуЧтобы обойти эту проблему, нужно определить state в терминах D, то есть

function updateDataStructure<K extends DataContainerProps, D extends ArrayElement<StateDataContainer[K]>>
    (state: { data: { [key in K]: D[] } }, structureName: K, elem: D)
    : D[] {

    state.data[structureName].push(elem);
    return state.data[structureName];
}

площадка TypeScript: ссылка

0 голосов
/ 09 марта 2019

Ваш параметр structureName имеет тип 'aa' | 'bb', а тип elem - AA | BB.TypeScript в настоящее время не сужает генерики так, как вы ожидаете (см. # 23578 , # 24085 ).

В вашем примере обратите внимание, что типы ввода / выводавсе еще верны (вы не можете передать line, если ваш structureName равен 'aa').Итак, если вы готовы пожертвовать некоторой безопасностью типов в реализации вашей функции, вот вам взлом, чтобы разблокировать вас:

     state.data[structureName as 'aa'].push(elem as AA);
...