简体   繁体   English

如何区分字符串文字类型的联合?

[英]How to discriminate unions of string literal types?

I'm trying to implement a declarative API for writing CSS styles using MDN metadata and TypeScript.我正在尝试实现一个声明性 API 用于编写 CSS styles 使用 MDN3344CF685F39BEDZ styles My goal is to cast generic string values to string literal types so I can user it later on, either code-generating "const" files from json metadata or dynamically.我的目标是将通用字符串值转换为字符串文字类型,以便我以后可以使用它,从 json 元数据或动态代码生成“const”文件。

const data = [
  { type: 'at-rule', value: '@media' },
  { type: 'css-property', value: 'color' }
]

const createPrefix = <P extends string>(prefix: P) => <A extends string>(it: A) => `${prefix}: ${it};` as `${P}: ${A}`

const color = createPrefix('color') // works

// usage
const blueText = color('blue')
// const blueText: 'color: blue;'
Usage用法

Ideally we'd generate string literals similar (in format) to the value of cssText property of a CSSStyleRule .理想情况下,我们会生成与CSSStyleRulecssText属性值类似(格式上)的字符串文字。

import * as s from './composers'

s.of('button-primary').color('blue').background('white')
// .button-primary { color: blue; background: white; }

I've been struggling for a whole week with this.我已经为此苦苦挣扎了整整一周。 Probably I understand TypeScript so badly that I'm trying to do something undoable or felt into a bad practice.可能我对 TypeScript 的理解非常糟糕,以至于我正在尝试做一些无法撤消的事情,或者觉得这是一种不好的做法。 Regardless, here's what I'm trying to do:无论如何,这就是我正在尝试做的事情:

type AtRule =
  | '@color-profile'
  | '@charset'
  | '@counter-style'
  | '@document'
  | '@font-face'
  | '@font-feature-values'
  | '@import'
  | '@property'
  | '@keyframes'
  | '@viewport'
  | '@media'
  | '@namespace'
  | '@page'
  | '@supports'

const createPrefix =
  <A extends AtRule>(a: A) =>
  <B extends string>(b: B) => {
    return `${a}: ${b};` as const
  }

function getComposer<T extends AtRule>(s: T) {
  const value: T = s
  return createPrefix(value)
}

const data = [{ value: '@charset' }, { value: '@document' }] as const

const prefixes = data.map(d => getComposer(d.value))

const something = prefixes[0]('blue')
console.log(something) // → '@charset: blue;'
// const something: "@charset: blue;" | "@document: blue;"

What am I missing?我错过了什么?

I think you need to go through generics:我认为您需要 go 到 generics:

  const createPrefix = <A extends string>(a: A) => <B extends string>(b: B) => {
    return `${a}:${b}` as `${A}:${B}`
  }
  function getComposer<T extends Prop>(s: T) {
    switch (s.value) {
      case "background":
        return createPrefix<T['value']>(s.value)
      case "color":
        return createPrefix<T['value']>(s.value)
      default:
        return assertNever(s);
    }
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM