From 94cc7cbedb145ddc5e95442587f02186d3269789 Mon Sep 17 00:00:00 2001 From: Carson Full Date: Mon, 3 Jun 2024 13:15:46 -0500 Subject: [PATCH] Fix `MadeEnum<'a'>` to be compatible with `MadeEnum` Disallow direct access to member values when referencing generically ```ts let Color: MadeEnum<'red' | 'blue'>; let AnEnum: MadeEnum; ``` Without this change `Color` can't be assigned to `AnEnum`, because the TS defines the member values as an index: `{ [x: string]: string }`. So since one has an index accessor and one doesn't, making them incompatible. The `entry()` arg type change also allows them to be compatible. Without it TS thinks something like `entry()` fn accepting any `string` is not compatible with `entry()` fn only accepting `'red' | 'blue'`. Using another generic at the function level somehow works around this, while still maintaining all the strictness. --- src/common/make-enum.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/common/make-enum.ts b/src/common/make-enum.ts index 6f939080ec..637d6c8b58 100644 --- a/src/common/make-enum.ts +++ b/src/common/make-enum.ts @@ -12,13 +12,18 @@ export type MadeEnum< Values extends string, ValueDeclaration = EnumValueDeclarationShape, Extra = unknown, -> = { - readonly [Value in Values & string]: Value; -} & EnumHelpers< +> = EnumHelpers< Values, ValueDeclaration & ImplicitValueDeclarationShape > & - Readonly; + Readonly & + // Allow direct access to the values if they're strict. + // For generic `string` we don't allow this. + // This allows strict values to be compatible with generic values. + // MadeEnum = MadeEnum + (string extends Values + ? unknown // ignore addition + : { readonly [Value in Values & string]: Value }); interface EnumOptions< ValueDeclaration extends EnumValueDeclarationShape, @@ -217,7 +222,7 @@ type NormalizedValueDeclaration = interface EnumHelpers { readonly values: ReadonlySet; readonly entries: ReadonlyArray>; - readonly entry: (value: Values) => Readonly; + readonly entry: (value: V) => Readonly; readonly has: ( value: In & {}, ) => value is In & Values & {};