В проекте TypeScript у меня есть массив контейнеров, которые несут свойство type
и некоторые дополнительные данные, в зависимости от их типа.
type Container<Type extends string> = {
type: Type;
}
type AContainer = Container<"a"> & {
dataA: number;
}
type BContainer = Container<"b"> & {
dataB: boolean;
}
const data: (AContainer | BContainer)[] = [
{ type: "a", dataA: 17 },
{ type: "b", dataB: true }
];
Моя цель - написать функцию, которая позволит мне выбрать элемент из этого массива по его type
, с полной безопасностью типа. Примерно так:
const getByType = <T extends string>(data: Container<string>[], type: T): Container<T> => {
for (const c of data) {
if (c.type === type) return c;
}
throw new Error(`No element of type ${type} found.`);
};
const dataA: AContainer = getByType(data, "a");
Проблема заключается в попытке убедить TypeScript в том, что функция безопасна для типов, а возвращаемое значение является элементом исходного массива и имеет запрошенный тип.
Вот моя лучшая попытка:
const getByType = <ContainerType extends Container<string>, Type extends string>(data: (ContainerType & Container<string>)[], type: Type): ContainerType & Container<Type> => {
for (const c of data) {
if (c.type === type) return c;
}
throw new Error(`No element of type ${type} found.`);
};
Однако TypeScript не понимает, что сравнение c.type === type
гарантирует, что Container<string>
превращается в Container<Type>
, и что тип возврата в примере вызова AContainer | (Container<"b"> & { dataB: boolean; } & Container<"a">)
, равно AContainer
из-за конфликта в Container<"b"> & Container<"a">
. Первая проблема может быть решена путем использования предиката типа в качестве следующего в следующем блоке кода (хотя это похоже на обман), но я не нашел решения для второй проблемы.
const isContainer = <Type extends string>(c: Container<string>, type: Type): c is Container<Type> => {
return typeof c === "object" && c.type === type;
};
Есть ли способ заставить это работать? Я бы предпочел, чтобы как getByType
, так и само его использование были безопасными с точки зрения типов, но если это невозможно, я бы хотел, чтобы по крайней мере использование getByType
не требовало небезопасных утверждений типа.
I Можно изменить определения типов контейнеров, но фактические данные фиксированы. (Для фона: xml2 js XML парсер.)