Eu tenho vários enums
const enum E1 {
CLOSED = 'CLOSED',
OPEN = 'OPEN',
IN_PROGRESS = 'IN_PROGRESS',
}
const enum E2 {
OPEN = 'OPEN',
CLOSED = 'CLOSED',
IN_PROGRESS = 'IN_PROGRESS',
}
const enum E3 {
OPEN = 'OPEN',
IN_PROGRESS = 'IN_PROGRESS',
}
Quero criar uma função que aceite um enum que um dos valores deva ser 'CLOSED'
const func = (value: 'CLOSED') => {...}
interface Data1 {status: E1}
interface Data2 {status: E2}
interface Data3 {status: E3}
const o1: Data1 = {status: E1.CLOSED}
const o2: Data2 = {status: E2.OPEN}
const o3: Data3 = {status: E3.OPEN}
func(o1.status) // this should be valid. status is type E1, it contains 'CLOSED'
func(o2.status) // this should be valid. status is type E2, it contains 'CLOSED'
func(o3.status) // this should be invalid. status type is E3, it does not contain 'CLOSED'
Não gera um erro para cada um deles:
Argument of type E... is not assignable to parameter of type "CLOSED"
Não quero que essa função saiba sobre cada enum, pois são muitos. É por isso que usei o tipovalue: 'CLOSED'
Preciso de algo como um enum comum para fazer com que outros o estendam de alguma forma, para que eu possa usar seu tipo no argumento em vez de 'CLOSED'?
Versão datilografada: 4.9.4
Enums têm algum comportamento de sistema de tipo estranho que torna isso mais complicado. Por enquanto, vamos apenas usar tipos literais de string simples em vez de enums e então podemos voltar a isso. Podemos substituir suas definições de enum por
const
objetos -asserted e aliases de tipo por enquanto.E1
parecee os outros são análogos.
O tipo
(value: "CLOSED") => void
não funciona para você porque isso significa que tudo o que você passavalue
deve ser atribuível ao tipo literal"CLOSED"
. Isso está ao contrário do que você deseja. Em vez disso, você quer dizer que isso"CLOSED"
deve ser atribuível ao tipo devalue
. Em vez de limitar o tipovalue
de cima (por exemplo, deve ser"CLOSED"
ou algum tipo mais restrito), você deseja limitá-lo de baixo (por exemplo, deve ser"CLOSED"
ou algum tipo mais amplo ).E, infelizmente, o TypeScript não oferece suporte nativo a essas restrições de tipo de limite inferior . As restrições do TypeScript (por exemplo,
T extends U
) são limites superiores. Há uma solicitação de recurso aberta em microsoft/TypeScript#14520 para permitir restrições de limite inferior (por exemplo,T super U
). Se isso fizesse parte do TypeScript, eu diria que você poderia escrevere pronto. Você estaria restringindo
value
de cimastring
e de baixo por"CLOSED"
. Mas você não pode fazer isso diretamente.O que você pode fazer como solução alternativa é aproveitar os tipos condicionais em sua restrição. Conceitualmente
T super "CLOSED"
é o mesmo que"CLOSED" extends T
. Portanto, se pudermos reescrever nossa restrição para que ela"CLOSED" extends T
seja aplicada, ela poderá se comportar como você deseja:E funciona. Quando você liga
func(o1.status)
,T
é inferido comoE1
. Então a restrição se torna'CLOSED' extends E1 ? string : never
, que se reduz astring
, e assim a restrição éT extends string
atendida e bem-sucedida.Mas quando você liga
func(o3.status)
,T
é inferido comoE3
. Então a restrição se torna'CLOSED' extends E3 ? string : never
, que se reduz ao tiponever
e , portanto, a restrição éT extends never
which não atendida e falha.É assim que eu faria se você não estivesse usando enums. Enums tornam tudo mais complicado, porque eles próprios são considerados subtipos adequados de seus valores. Então
E1.CLOSED extends "CLOSED"
é verdade, mas"CLOSED" extends E1.CLOSED
é falso:E isso significa
"CLOSED"
que não é um limite inferiorE1
, mesmo que seja um limite inferior na string que os tipos literaisE1
representam. Em vez de tentar dissecar enums, usarei um truque para ampliar enums de string para seus tipos literais de string: basta usar tipos literais de modelo para serializá-los:E o mesmo
"CLOSED"
ocorre com um limite inferiorE1Serialized
. Isso significa que podemos mudarfunc
para serE tudo funciona:
Link do Playground para o código
Isso poderia funcionar para você?
Confira esta demonstração