#typescript
#typescript
Вопрос:
Я пытаюсь написать функцию, которая вернет значение помеченного объединения, но я хочу, чтобы возвращаемый тип уточнялся в зависимости от тега. Например,
type Token = {kind: " "} | {kind: "var", text: string};
let tokens: Token[] = [{kind: "var", text: "x"}, {kind: " "}, {kind: "var", text: "y"}];
function pullToken<Kind extends Token["kind"]>(kind: Kind): Extract<Token, {kind: Kind}> {
let token = tokens.shift();
if (typeof token === "undefined" || token.kind !== kind) {
throw "oops";
}
return token;
}
let token = pullToken("var");
console.log(token.text);
выдает мне следующую ошибку
TS2322: Type 'Token' is not assignable to type 'Extract<{ kind: " "; }, { kind: Kind; }> | Extract<{ kind: "var"; text: string; }, { kind: Kind; }>'.
Type '{ kind: " "; }' is not assignable to type 'Extract<{ kind: " "; }, { kind: Kind; }> | Extract<{ kind: "var"; text: string; }, { kind: Kind; }>'.
Type '{ kind: " "; }' is not assignable to type 'Extract<{ kind: "var"; text: string; }, { kind: Kind; }>'.
Я ожидал, что token
условие в if
инструкции будет уточнено, но это не так. Есть ли способ добиться этого?
Ответ №1:
Известной проблемой TypeScript является то, что вы не можете использовать анализ потока управления для сужения параметров универсального типа (см. microsoft / TypeScript#24085) или значений, тип которых зависит от параметров универсального типа (см. microsoft / TypeScript # 13995).
Обычно, если у вас есть значение типа объединения (типа Token
) и вы выполняете для него защиту типа, тогда компилятор будет использовать анализ потока управления, чтобы сузить кажущийся тип этого значения до некоторого элемента или членов исходного объединения (типа {kind: "var", text: string}
). Это все хорошо, когда значение относится к неродовому типу.
Но если ваше значение (like token
) имеет тип, который зависит от параметра универсального типа, ограниченного объединением (like Kind extends Token
), то этого не произойдет. Это не сужает тип значения, и это определенно не сужает параметр типа (например Kind
) сам по себе.
Это сложная проблема для решения, поскольку во многих случаях компилятору было бы некорректно сужать параметр type, и даже там, где это могло бы быть правильным, это потребовало бы, чтобы компилятор потратил больше времени на выполнение некоторого анализа типов более высокого порядка. Возможно, в конечном итоге язык позволит вашему коду просто «работать». На данный момент вам нужно обойти это.
Компилятор не может проверить, что это token
относится к типу Extract<Token, {kind: Kind}>
. Но вы написали код, который должен (я надеюсь) сделать это правдой. Поскольку вы знаете что-то, чего не знает компилятор о типе token
, вы можете использовать утверждение типа, чтобы просто сообщить об этом:
return token as Extract<Token, {kind: Kind}>;
Это приведет к отключению ошибки. Обратите внимание, что бремя проверки безопасности типов внутри реализации pullToken()
теперь лежит на вас; если вы допустили ошибку или солгали (например, return {kind: " "} as Extract<Token, {kind: Kind}>;
компилятор не поймает это. Так что будьте осторожны.