我有兩個要合并的列舉。
enum Color {
RED = 'red',
BLUE = 'blue',
}
enum Shape {
CIRCLE = 'circle',
SQUARE = 'square',
}
但是,當我宣告一個聯合型別并嘗試參考一個成員時:
type ColorShape = Color | Shape;
const test: ColorShape = ColorShape.RED;
我得到一個編譯時錯誤:
TS2693:“ColorShape”僅指一種型別,但在此處用作值。
為什么是這樣?列舉的聯合不也是列舉本身嗎?
uj5u.com熱心網友回復:
重要的是要記住 TypeScript型別之間的 TypeScript 差異,它們存在于編譯時并從發出的 JavaScript 代碼中洗掉;和 JavaScript values,它們也存在于運行時并出現在發出的 JavaScript 代碼中。TypeScript 試圖跟蹤值和型別,但 JavaScript 只知道值。
大多數情況下,當您在 TypeScript 中進行宣告時,您要么將值帶入作用域,要么將型別帶入作用域,但不能兩者兼而有之:
// values
const foo = 123;
function bar() { }
// types
type Baz = { a: number };
interface Qux { b: string }
在上面,foo和bar是存在于發出的 JavaScript 中的值,而Baz和Qux是被擦除的型別。
請注意,值和型別具有完全不同的命名空間;你可以有一個同名的值和型別,它們不必相互關聯。編譯器不會混淆它們:
// separate namespaces
const Xyz = { z: 1 };
type Xyz = { y: string };
這Xyz是一個值的名稱(具有值為 的z屬性的物件1)和型別的名稱(具有y其值型別的屬性的物件型別string)。這些是無關的。如果我寫const uvw = Xyz;,那么我正在撰寫使其成為 JavaScript 的值級代碼,因此Xyz值就是值。如果我寫interface Rst extends Xyz { },那么我正在撰寫從 JavaScript 中洗掉的型別級代碼,因此Xyz是型別。
好的,所以大多數情況下,當您宣告某個命名事物時,該事物要么是型別,要么是值,但不是兩者兼而有之。但是有兩個宣告確實會產生同名的值和型別:class宣告和宣告:enum
// both
class Abc { c = "" }
enum Def { D = "e" }
這里的名稱既是指可以在運行時參考Abc的類建構式值new Abc(),例如,也指可以在編譯時用來談論類實體的類實體型別const instance: Abc = ...,例如. 如果你寫const instance: Abc = new Abc(),你首先指的是型別,然后是值。
類似地,該名稱既指代您可以在運行時參考Def的列舉物件值Def.D === "e",例如,也指代列舉型別,它是列舉成員值型別的聯合,例如const def: Def = ...。如果你寫const def: Def = Def.D,你首先指的是型別,然后是值。
現在,雖然class是 JavaScript 的一部分(自 ES2015 起),enum但不是。因此,當您撰寫enum編譯器時,編譯器實際上會將其降級為某些 JavaScript。讓我們看看你的Color列舉:
enum Color {
RED = 'red',
BLUE = 'blue',
}
這或多或少會編譯成類似的東西
const Color = {
RED: "red",
BLUE: "blue"
}
它只是一個普通的物件。同時,型別Color的行為類似于
type Color = "red" | "blue";
這并不完全正確,因為enums 是“標記的”,因此您不能只為它們分配一個字串,但它對于我們的目的來說已經足夠接近了。因此,當您的enum Color宣告類似于上述值和型別宣告的組合時。
現在,當你寫
type ColorShape = Color | Shape;
您正在創建一個ColorShape 型別。But type ColorShape = ...is not an enum,所以你所做的就是創建型別。這里沒有相應的價值存在。enum在創建類似組合的東西時,您只完成了一半的作業。如果您想要相應的值,則必須手動創建它。
你想要的是看起來像的東西{RED: "red", BLUE: "blue", CIRCLE: "circle", SQUARE: "square"}。有多種方法可以獲取Color價值和Shape價值并制作這樣的物件。
您可以使用以下Object.assign()方法:
const ColorShape = Object.assign({}, Color, Shape);
// const ColorShape: typeof Color & typeof Shape
或者你可以使用物件傳播:
const ColorShape = { ...Color, ...Shape };
/* const ColorShape: {
CIRCLE: Shape.CIRCLE;
SQUARE: Shape.SQUARE;
RED: Color.RED;
BLUE: Color.BLUE;
} */
無論哪種方式都應該有效。完成此操作后,現在,您可以(大部分)使用ColorShape,就好像它是由andenum組成的組合:ColorShape
const test: ColorShape = ColorShape.RED; // okay
請注意,我說的是“大部分”。該enum宣告還將更多型別納入范圍;它實際上充當了namespace您可以使用虛線路徑將列舉值作為型別本身的地方:
interface RedState {
name: string;
color: Color.RED // <-- this is a type
}
但這不會發生在您的任何一個ColorShape定義中:
interface RedState {
name: string;
color: ColorShape.RED // error!
// 'ColorShape' only refers to a type, but is being used as a namespace here.
}
你可能不在乎這個。如果您以某種方式這樣做,那么我知道如何做到這一點的唯一方法是手動創建您的ColorShape命名空間并從中匯出每種型別:
namespace ColorShape {
export type CIRCLE = typeof ColorShape.CIRCLE;
export type SQUARE = typeof ColorShape.SQUARE;
export type RED = typeof ColorShape.RED;
export type BLUE = typeof ColorShape.BLUE;
}
不是很好,可能不是你需要的任何東西。但是為了完整起見,我想我只是表明在enums 的引擎蓋下還有更多的事情發生。
Playground 代碼鏈接
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/433063.html
上一篇:撰寫通用的多維Map型別
