AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 78783536
Accepted
Oktay Yuzcan
Oktay Yuzcan
Asked: 2024-07-23 20:51:06 +0800 CST2024-07-23 20:51:06 +0800 CST 2024-07-23 20:51:06 +0800 CST

Função comum para diferentes enumerações

  • 772

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

typescript
  • 2 2 respostas
  • 41 Views

2 respostas

  • Voted
  1. Best Answer
    jcalz
    2024-07-23T23:57:25+08:002024-07-23T23:57:25+08:00

    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 constobjetos -asserted e aliases de tipo por enquanto. E1parece

    const E1 = {
        CLOSED: 'CLOSED',
        OPEN: 'OPEN',
        IN_PROGRESS: 'IN_PROGRESS',
    } as const
    type E1 = typeof E1[keyof typeof E1]
    

    e os outros são análogos.


    O tipo (value: "CLOSED") => voidnão funciona para você porque isso significa que tudo o que você passa valuedeve 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 de value. Em vez de limitar o tipo valuede 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 escrever

    // not valid TS, don't try this:
    const func = <T extends string super 'CLOSED'>(value: T) => { }
    

    e pronto. Você estaria restringindo valuede cima stringe 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 Tseja aplicada, ela poderá se comportar como você deseja:

    const func = <T extends 'CLOSED' extends T ? string : never>(value: T) => { }
    
    func(o1.status)   // okay
    func(o2.status)   // okay
    func(o3.status)   // error
    func(Math.random() < 0.5 ? "CLOSED" : "XYZ"); // okay
    func(Math.random() < 0.5 ? "ABC" : "XYZ"); // error
    

    E funciona. Quando você liga func(o1.status), Té inferido como E1. Então a restrição se torna 'CLOSED' extends E1 ? string : never, que se reduz a string, e assim a restrição é T extends stringatendida e bem-sucedida.

    Mas quando você liga func(o3.status), Té inferido como E3. Então a restrição se torna 'CLOSED' extends E3 ? string : never, que se reduz ao tipo nevere , portanto, a restrição é T extends neverwhich 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:

    type Yes = E1.CLOSED extends "CLOSED" ? true : false
    //   ^? type Yes = true
    type No = "CLOSED" extends E1.CLOSED ? true : false
    //   ^? type No = false
    

    E isso significa "CLOSED"que não é um limite inferior E1, mesmo que seja um limite inferior na string que os tipos literais E1 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:

    type E1Serialized = `${E1}`
    //   ^? type E1Serialized = "CLOSED" | "OPEN" | "IN_PROGRESS"
    

    E o mesmo "CLOSED"ocorre com um limite inferior E1Serialized. Isso significa que podemos mudar funcpara ser

    const func = <T extends 'CLOSED' extends `${T}` ? string : never>(value: T) => { }
    

    E tudo funciona:

    func(o1.status)   // okay
    func(o2.status)   // okay
    func(o3.status)   // error
    func(Math.random() < 0.5 ? "CLOSED" : "XYZ"); // okay
    func(Math.random() < 0.5 ? "ABC" : "XYZ"); // error
    

    Link do Playground para o código

    • 1
  2. Remo H. Jansen
    2024-07-23T22:52:17+08:002024-07-23T22:52:17+08:00

    Isso poderia funcionar para você?

    1. Crie tipos que sejam tipos de união (de tipos literais de string) em vez de enums:
    type CLOSED<T extends string> = `${T}.CLOSED`;
    type OPEN<T extends string> = `${T}.OPEN`;
    type IN_PROGRESS<T extends string> = `${T}.IN_PROGRESS`;
    
    type EWithClosed<T extends string> = CLOSED<T> | OPEN<T> | IN_PROGRESS<T>;
    type EWithoutClosed<T extends string> = Exclude<EWithClosed<T>, CLOSED<T>>;
    
    1. Crie valores que possam ser usados ​​em tempo de execução:
    const makeClosed = <T extends string>(k: T): CLOSED<T> => `${k}.CLOSED`;
    const makeOpen = <T extends string>(k: T): OPEN<T> => `${k}.OPEN`;
    const makeInProgress = <
        T extends string
    >(k: T): IN_PROGRESS<T> => `${k}.IN_PROGRESS`;
    
    const makeWithClosed = <T extends string>(k: T) => ({
      CLOSED: makeClosed(k),
      OPEN: makeOpen(k),
      IN_PROGRESS: makeInProgress(k)
    });
    
    const makeWithoutClosed = <T extends string>(k: T) => ({
      CLOSED: makeClosed(k),
      OPEN: makeOpen(k)
    });
    
    const E1 = makeWithClosed("E1");
    const E2 = makeWithClosed("E2");
    const E3 = makeWithoutClosed("E3");
    
    1. Podemos então usar os tipos e valores:
    const o1 = {status: E1.CLOSED }
    const o2 = {status: E2.OPEN }
    const o3 = {status: E3.OPEN }
    
    const func = <T extends string>(value: CLOSED<T>) => {
      // ...
    }
    
    func(o1.status)   // OK
    func(o2.status)   // ERROR
    func(o3.status)   // ERROR
    

    Confira esta demonstração

    • 0

relate perguntas

  • Por que usamos colchetes `[]` em condicionais?

  • Problema do Nestjs em relação aos módulos

  • Como obter metadados datilografados de um arquivo?

  • Como converter array em tipo const de retorno de objeto assim?

  • "Nenhuma sobrecarga corresponde" minha chamada Object.assign(); como posso corrigir isso?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle?

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Quando devo usar um std::inplace_vector em vez de um std::vector?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Marko Smith

    Estou tentando fazer o jogo pacman usando apenas o módulo Turtle Random e Math

    • 1 respostas
  • Martin Hope
    Aleksandr Dubinsky Por que a correspondência de padrões com o switch no InetAddress falha com 'não cobre todos os valores de entrada possíveis'? 2024-12-23 06:56:21 +0800 CST
  • Martin Hope
    Phillip Borge Por que esse código Java simples e pequeno roda 30x mais rápido em todas as JVMs Graal, mas não em nenhuma JVM Oracle? 2024-12-12 20:46:46 +0800 CST
  • Martin Hope
    Oodini Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores? 2024-12-12 06:27:11 +0800 CST
  • Martin Hope
    sleeptightAnsiC `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso? 2024-11-09 07:18:53 +0800 CST
  • Martin Hope
    The Mad Gamer Quando devo usar um std::inplace_vector em vez de um std::vector? 2024-10-29 23:01:00 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST
  • Martin Hope
    MarkB Por que o GCC gera código que executa condicionalmente uma implementação SIMD? 2024-02-17 06:17:14 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve