以下代碼按預期作業(沒有錯誤),
export type AOrB = 'a' | 'b';
export const a: AOrB = 'a';
export const b: AOrB = 'b';
export const obj: ObjWithAOrB = {
[a]: 'foo',
[b]: 'bar',
};
但是,當它被分成 3 個檔案時,obj說鍵是字串型別會出現型別錯誤。這是什么原因造成的?不確定是否相關,"isolatedModules": false"。
這會產生一個錯誤:
// types.ts
export type AOrB = 'a' | 'b';
export type ObjWithAOrB = {
[_ in AOrB]: any;
};
// constants.ts
import { AOrB } from './types';
export const a: AOrB = 'a';
export const b: AOrB = 'b';
// obj.ts
import { a, b } from './constants';
import { ObjWithAOrB } from './types';
export const obj: ObjWithAOrB = {
[a]: 'foo',
[b]: 'bar',
};
錯誤:
Type '{ [x: string]: string; }' is missing the following properties from type 'ObjWithAOrB': a, b
為什么常量被“降級”為字串?
uj5u.com熱心網友回復:
它在您的第一個示例中起作用的原因是,當您撰寫此代碼時:
export const a: AOrB = 'a';
export const b: AOrB = 'b';
TypeScript在分配時使用控制流縮小。對于代碼的在同一范圍內,其余,表觀型別的a和b將被縮小從聯合型別 AOrB的單個文本型別 "a"和"b"分別。您可以通過 IntelliSense 看到:
a // const a: "a"
b // const b: "b"
因此,當您obj從同一范圍匯出時,編譯器知道a是"a"和b是"b",因此計算出的鍵[a]和[b]是單個字串文字a和b:
export const obj: ObjWithAOrB = {
[a]: 'foo',
[b]: 'bar',
}; // okay
將此與跨模塊范圍拆分時發生的情況進行比較。在您的constants.ts模塊中,aand的型別b被視為"a"and "b"。但是當你import { a, b } from './constants',沒有從范圍縮小的控制流constants.ts持續存在。匯入的常量a和b常量僅被視為您將它們注釋為的型別AOrB:
import { a, b } from './constants';
a // (alias) const a: AOrB
b // (alias) const b: AOrB
編譯器完全不知道,a并且b可能比它們宣告的型別更窄。控制流縮小不會像您期望的那樣跨范圍。一般來說,這樣做的成本會高得令人望而卻步,因為在大多數情況下,編譯器不希望弄清楚如何對不同范圍的控制流進行建模。 您知道 aconst不能重新分配,因此由于分配而導致的任何控制流變窄都必須在其他范圍內有效,但編譯器不會對此進行特殊處理;如果這樣做,則需要開始在所有地方跟蹤此類縮小,并且在 99% 的現有代碼中都無關緊要。這個問題是棘手的;有關此類事情的有趣討論,請參閱microsoft/TypeScript#9998, 控制流分析中的權衡。可以想象提交一個功能請求,要求const從模塊范圍匯出,以保持跨邊界的控制流縮小,但我懷疑它會得到任何牽引力,因為鏈接問題中列出的原因。
繼續:現在您的物件文字有兩個計算鍵,每個鍵都是字串文字的聯合。在這種情況下,編譯器放棄將結果表示為物件型別的某種聯合,而是將鍵一直擴展到string. 這是microsoft/TypeScript#13948 的主題。但是,即使該問題得到解決,您所能希望的最好的{a: string} | {b: string} | {a: string; b: string}型別也是 ,并且該型別不可分配給ObjWithAOrB它肯定需要兩個鍵。
export const obj: ObjWithAOrB = {
[a]: 'foo',
[b]: 'bar',
}; // error!
這就是正在發生的事情以及原因。至于你應該怎么做:我會說你要么想要a,要么b被外部視為"a"和"b"分別,在這種情況下,你不應該將它注釋為更廣泛的東西:
// constants.ts
export const a = 'a';
export const b = 'b';
// obj.ts
import { a, b } from './constants';
import { ObjWithAOrB } from './types';
export const obj: ObjWithAOrB = {
[a]: 'foo',
[b]: 'bar',
}; // okay
或者您確實希望a并被b視為在AOrB外部,在這種情況下,您需要向編譯器斷言a真正屬于型別"a"并且b真正"b"屬于物件字面量內部的型別:
// obj.ts
import { a, b } from './constants';
import { ObjWithAOrB } from './types';
export const obj: ObjWithAOrB = {
[a as "a"]: 'foo',
[b as "b"]: 'bar',
}; // okay
Personally I'd go with the first approach. You might say "but I want the compiler to ensure that a and b are assignable to AOrB", in which case that's a different issue: you're looking for microsoft/TypeScript#7481 because you want to check that a and b are assignable AOrB without widening to AOrB. You can work around it with a generic function (as mentioned in the linked issue), but I don't want to make this long answer even longer than it is by writing that out.
Playground link to code
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/326526.html
標籤:打字稿
