typescript - Generisk begränsning av formen `<T extends E>` där `E` är en `enum`?
Vad betyder något som:
function f<T extends E>(obj: T) {}
där E är någon enum som, säg
enum E {
X
}
Eftersom enum faktiskt inte kan utökas, kan inte denna begränsning tas bort och funktionen skrivas om till:
function f(obj: E) {}
PS: detta är koden i fråga.
Svar #1
Om du inte behöver typen T i din funktion, så har du rätt att den inte behöver vara generisk.
Men i filen du länkade används typen inom klassen.
Här är de relevanta delarna, med annat stuff borttaget:
abstract class ComputedEditorOption<K1 extends EditorOption, V> implements IEditorOption<K1, V> {
public readonly id: K1;
constructor(id: K1, deps: EditorOption[] | null = null) {
this.id = id;
//...
}
}
Detta säger att en id av typen EditorOption skickas till konstruktören. Detta sparas i den generiska klassen typen K1. Senare, när du får åtkomst till id-egenskapen, kommer den att returnera samma enum-alternativ som skickades till konstruktören. Detta kräver generics för att kunna fånga en underklass av ett enum (ett specifikt värde av det enum), och använda det i returvärdet av den funktionen, eller instansen av en klass.
För att klargöra med ditt eget exempel:
function f1(arg: MyEnum): { id: MyEnum } {
return { id: arg }
}
const a1 = f1(MyEnum.A) // Type: MyEnum
const b1 = f1(MyEnum.B) // Type: MyEnum
När du skriver argumentet som MyEnum här kan du inte använda det enum som angavs i returtypen, så det bästa du kan göra är att skriva returvärdet som MyEnum, eftersom du inte kan veta vilket värde av det enum som gavs.
Men när du använder en generisk:
function f2<T extends MyEnum>(arg: T): { id: T } {
return { id: arg }
}
const a2 = f2(MyEnum.A) // Type: MyEnum.A
const b2 = f2(MyEnum.B) // Type: MyEnum.B
Nu kan du använda exakt den angivna typen av argumentet i returtypen.
Detta är, mer eller mindre, vad koden du länkade gör.
Lekplats
Svar #2
Om en typ utökar en enum, betyder det att typen kan vara:
- ett specifikt värde av enum
- någon delmängd av värdena i uppräkningsvärdet
- alla möjliga värden av enum
Låt oss säga att vår enum har tre värden:
enum E {
X,
Y,
Z
}
function f<T extends E>(val: T) {}
När du anropar funktionen f kan du bara skicka ett värde av E åt gången. Men det betyder inte att den generiska T bara kan vara ett värde av E. Om du manuellt specificerar den generiska T, är alla dessa giltiga:
f<E>(E.X);
f<E.X>(E.X);
f<E.X | E.Y>(E.X);
f<Exclude<E, E.Y>>(E.X);
I varje fall är E.X tilldelningsbar till T (det vill säga, E.X är lika med eller smalare än T) och T extends E (det vill säga, T är lika med eller smalare än E).
Lekplatslänk
