Вывод типа в классе factory

#typescript #type-inference

#typescript #вывод типа

Вопрос:

У меня есть класс DataHolder для управления информацией о точках и линиях. У DataHolder есть 2 дочерних класса. Каждый дочерний класс управляет одним типом данных (число, number[])

 const enum DataType { POINT, LINE }

class DataHolder<T> {
    protected type: DataType;
    protected data: T[] = [];

    constructor(type: DataType) { this.type = type; }

    public add(data: T) { this.data.push(data); }

    public clear() { this.data.length = 0; }

    public static factory(type: DataType) {
        switch (type) {
            case DataType.POINT: return new PointHolder();
            case DataType.LINE: return new LineHolder();
            default: return undefined;
        }
    }
}
class PointHolder extends DataHolder<number> {
    constructor() { super(DataType.POINT); }
}
class LineHolder extends DataHolder<number[]> {
    constructor() { super(DataType.LINE); }
}
type holders = PointHolder | LineHolder;
  

Далее, у меня есть перегруженные функции для получения и заполнения де-данных:

 const holders = new Map<DataType, holders>();
const currentTypes: DataType[] = [];

function getDataHolder(type: DataType.POINT): PointHolder;
function getDataHolder(type: DataType.LINE): LineHolder;
function getDataHolder(type: DataType): holders {
    if (!holders.has(type)) {
        holders.set(type, DataHolder.factory(type));
        currentTypes.push(type);
    }
    return holders.get(type);
}

function add(type: DataType.POINT, ids: number): void;
function add(type: DataType.LINE, ids: number[]): void;
function add(type: DataType, ids: number | number[]): void {
    let hldr: holders;
    switch (type) {
        case DataType.POINT:
            hldr = getDataHolder(type);
            hldr.add(ids);      // Argument of type 'number | number[]' is not assignable to parameter of type 'number'.
            break;
        case DataType.LINE: {
            hldr = getDataHolder(type);
            hldr.add(ids);      // Argument of type 'number | number[]' is not assignable to parameter of type 'number[]'.
            break;
        }
        default:
            break;
    }
}
function clear() {
    for (const type of currentTypes) {
        const hldr = getDataHolder(type); // No overload matches this call
        hldr.clear();
    }
}
  

Каков правильный способ определения перегруженных функций «getDataHolder» и «add» для вывода правильных типов передаваемых аргументов.

Ответ №1:

typescript имеет некоторое странное поведение, когда дело доходит до типов объединения

добавить функцию: typescript не может вывести тип на основе другого аргумента. но он может выводить свойства объекта, чтобы вы могли записать его как

 interface pointParams {
    type: DataType.POINT; ids: number;
}

interface lineParams {
    type: DataType.LINE; ids: number[];
}

type addFunc = (para: pointParams | lineParams) => void

let add:addFunc = function(para) {
    let hldr: holders;
    switch (para.type) {
        case DataType.POINT:
            hldr = getDataHolder(para.type);
            hldr.add(para.ids);      // Argument of type 'number | number[]' is not assignable to parameter of type 'number'.
            break;
        case DataType.LINE: {
            hldr = getDataHolder(para.type);
            hldr.add(para.ids);      // Argument of type 'number | number[]' is not assignable to parameter of type 'number[]'.
            break;
        }
        default:
            break;
    }
}
  

очистить функцию: это тоже довольно странная функция, которую знает typescript DataType == DataType.POINT | DataType.LINE , но она не знает DataType.POINT | DataType.LINE == DataType , поэтому вам нужно объявить getDataHolder, чтобы она работала

 function getDataHolder(type: DataType.POINT): PointHolder;
function getDataHolder(type: DataType.LINE): LineHolder;
function getDataHolder(type: DataType): holders;
function getDataHolder(type: DataType): holders {
    if (!holders.has(type)) {
        holders.set(type, DataHolder.factory(type));
        currentTypes.push(type);
    }
    return holders.get(type);
}