У меня есть следующий машинописный код (ну, более сложный, потому что он написан на Vue, поэтому некоторые части реактивны).
type A = {
a: string;
}
type B = {
b: string;
}
const getObj = function(useA: boolean): A|B {
if (useA) {
return { a: 'test'} as A;
} else {
return { b: 'test'} as B;
}
}
const getKey = function(useA: boolean): 'a'|'b' {
if (useA) {
return 'a';
} else {
return 'b';
}
}
const getValue = function(useA: boolean): string {
return getObj(useA)[getKey(useA)];
}
В getValue
есть ошибка машинописного текста, потому что нельзя гарантировать, что объект из getObj
будет тем типом, для которого ключ возвращен из getKey
. Есть ли способ реорганизовать это с помощью дженериков или чего-то еще, чтобы избежать этой ошибки? В качестве альтернативы, могу ли я сказать машинописному тексту игнорировать ошибку (например, комментарии, которые отключают линтинг в строке)? Или как-то использовать утверждения типа? В реальном случае есть более двух возможностей, и использование гардов было бы грязным.
Я понимаю, почему машинописному тексту это не нравится, но я надеюсь либо найти способ сделать это, не относящийся к типу, либо сказать машинописному тексту, что меня устраивает риск/гарантия, что свойство будет там.
Также обратите внимание, что я не могу поместить общую функцию в A/B, чтобы просто вернуть значение, потому что в реальном коде я не контролирую эти типы/объекты.
Если вы действительно хотите провести рефакторинг, чтобы компилятор мог проверить безопасность типов, вам, вероятно, потребуется выполнить исправление для ms/TS#30581 , как описано в ms/TS#47109 , хотя наличие ввода boolean
делает это более странно и сложно. Похоже на эту версию, то есть 🙃, но она настолько близка, насколько мы можем здесь использовать проверку типов компилятора. Я, вероятно, предпочел бы просто использовать утверждения типа в этом случае, но если вы хотите, чтобы я написал ответ, объясняющий это, дайте мне знать.
Да, @jsejcksn, эта игровая площадка помогла. На самом деле, даже не требуется подробного ввода getObj и getKey... достаточно было изменить тело getValue на: return getObj(useA)[getKey(useA) as keyof typeof getObj]; Это, вероятно, не гарантирует безопасность, но это был самый простой ответ для устранения ошибки.
Вы можете сделать так, чтобы функция getObj возвращала универсальный тип T, а затем приводила возвращаемое значение, если вас устраивает риск того, что свойство может отсутствовать во время выполнения.
type A = {
a: string;
}
type B = {
b: string;
}
const getObj = function(useA: boolean): T {
return useA ? { a: 'test' } : { b: 'test' };
}
далее в коде:
const aObj = getObj(true) as A;
console.info(aObj.a);
const bObj = getObj(false) as B;
console.info(bObj.b);
Обратите внимание, что типы исчезают во время компиляции, поэтому, если во время выполнения по какой-либо причине свойство a не существует в aObj или b не существует в bObj, эти значения будут неопределенными, и вы будете передавать неопределенные значения, что может привести к ошибкам.
в качестве альтернативы, если вы хотите убедиться, что у объектов есть соответствующие ключи и значения, вы можете сделать что-то вроде этого:
const aObj = getObj(true);
if (aObj.a === undefined) {
// handle error
}
Для удобства, для функции getKey вы можете фактически ввести результат с типом, который возвращает только a или b следующим образом:
const ABKeys = ['a', 'b'] as const;
type ABKey = typeof ABKeys[number];
const getKey = function (useA: boolean): ABKey {
return useA ? ABKeys[0] : ABKeys[1];
}
Я не думаю, что этот подход на самом деле помогает в случае желания сделать: obj = getObj(variableObjType)[variableKey], где известно, что variableObjType и variableKey совпадают, но неизвестно, что они собой представляют.
Вы можете использовать утверждение типа, чтобы помочь компилятору понять корреляцию между возвращаемыми типами двух функций с одним и тем же входным параметром:
function getValue(useA: boolean): string {
return getObj(useA)[getKey(useA) as keyof typeof getObj];
// ^^^^^^^^^^^^^^^^^^^^^^
// type assertion
}
Вы также можете использовать сигнатуры перегрузки функций для предоставления более узких, конкретных типов возвращаемых значений, связанных с их входными параметрами — это улучшит правильность типов, а также улучшит опыт разработчиков при использовании редактора кода с интерактивной обратной связью (например, IntelliSense). в VS Code или на игровой площадке TypeScript).
Вот полный пример:
type A = { a: string };
type B = { b: string };
function getObj(useA: true): A;
function getObj(useA: false): B;
function getObj(useA: boolean): A | B;
function getObj(useA: boolean) {
return useA ? { a: "test" } : { b: "test" };
}
function getKey(useA: true): "a";
function getKey(useA: false): "b";
function getKey(useA: boolean): "a" | "b";
function getKey(useA: boolean) {
return useA ? "a" : "b";
}
function getValue(useA: boolean): string {
return getObj(useA)[getKey(useA) as keyof typeof getObj];
}
// Other examples:
const o1 = getObj(true);
//^? const o1: A
const o2 = getObj(false);
//^? const o2: B
const o3 = getObj(true as boolean);
//^? const o3: A | B
const k1 = getKey(true);
//^? const k1: "a"
const k2 = getKey(false);
//^? const k2: "b"
const k3 = getKey(true as boolean);
//^? const k3: "a" | "b"