【问题标题】:String literals incompatible with string type when intersecting object与对象相交时字符串文字与字符串类型不兼容
【发布时间】:2026-02-26 12:45:01
【问题描述】:

我正试图将我的头包裹在 Flow 周围。

这按预期工作:

// @flow

type FontName = string
const doSomethingWithFontName = (fontName: FontName) => console.log(fontName)

type StrictFontName = 'Arial' | 'Verdana' | 'Times'
const doSomethingWithStrictFontName = (fontName: StrictFontName) => doSomethingWithFontName(fontName)

doSomethingWithStrictFontName('Times')

doSomethingWithStrictFontName 调用doSomethingWithFontName 工作正常。我猜 Flow 看到我的枚举 StrictFontName 只包含字符串,因此我可以使用字符串或 StrictFontName 调用 doSomethingWithFontName

但是将变量包装在对象中失败:

// @flow

type Typography = { 
  fontName?: string; 
}
const doSomethingWithTypography = (typography: Typography) => console.log(typography)

type StrictTypography = Typography & { 
  fontName?: 'Arial' | 'Verdana' | 'Times'; 
}
const doSomethingWithStrictTypography = (typography: StrictTypography) => {
  doSomethingWithTypography(typography)
}

doSomethingWithStrictTypography({fontName: 'Times'})

我只是在这里将我们的严格对象转发给一个更松散的函数,为什么这不起作用? Flow 可以告诉StrictTypographyfontName 仍然是一个字符串对吧?

我已阅读此thread 并想出了将超类型作为接口作为解决方法:

// @flow

// Interface instead of type
interface Styles { 
  fontName?: string; 
}

const doSomethingWithStyling = (styles: Styles) => console.log(styles)

type StrictStyles  = Styles & { 
  fontName?: 'Arial' | 'Verdana' | 'Times'; 
}
const doSomethingWithStrictStyling = (styles: StrictStyles) => {
  doSomethingWithStyling(styles)
}

doSomethingWithStrictStyling({fontName: 'Times'})

这似乎可行,但我不确定这是否是正确的方法。另外,如果我导入这个接口,我会得到一个Use of future reserved word in strict mode...

你可以找到游乐场here

【问题讨论】:

    标签: flowtype


    【解决方案1】:

    这里发生了两件事。

    通过定义交集Styles & { fontName?: 'Arial' | 'Verdana' | 'Times' },您可以创建一个类型,该类型可能具有属性fontName,该属性必须是string 类型,并且也是您输入的特定字符串之一。这是不可能的,因为如果字符串是'Verdana' 或其他东西,那么它也不能是任何其他 字符串。这是一个奇怪的概念,但基本上通过将类型缩小到特定字符串,你说它不能是其他字符串。通过将其设为string,您是在说它可以是任何字符串。无法满足此类型。

    它在您的第一个示例中起作用的原因是因为variance。基本上,第一个更广泛的函数接受string,但由于它是一个函数参数,它是逆变并且允许不太具体的类型,例如'foo''bar',是的,@987654333 @。 Flow 自动将函数参数类型解释为逆变,将函数返回类型解释为协变。

    所以你的意图可能是这样的:

    type StrictTypography = { 
      fontName?: 'Arial' | 'Verdana' | 'Times'; 
    }
    

    但是还是不行,因为虽然参数是逆变的,但是对象属性fontName不是,所以现在它只满足broadstrings和正好broad@ 987654337@s。要使其具有逆变性,请使用+ 符号:

    type Typography = {
      +fontName?: string; 
    }
    

    Completed example.

    【讨论】:

    • 我认为这是有道理的。因此,在此处添加+ 符号会将fontName 更改为接受宽字符串和其他可归结为宽字符串的字符串,例如'Arial' | 'Verdana' | 'Times' 之一。对吗?
    • 但是,这并没有改变我无法表达使字符串可选的方法这一事实。以下概念不能用流来表达:1)函数需要一个可能包含字体名称或不包含任何内容的对象。 2) 另一个函数需要一个对象,该对象可能包含三个预定义的 fontNames 之一或什么都不包含。 3) 第二个函数用它的参数调用第一个函数。
    • @Tieme + 使其接受相同的类型 (string) 和子类型。所有精确字符串都是string 的子类型,因为string 是所有单独字符串类型的集合。至于你的第二条评论,为什么不呢?也许我误解了,但这个例子不起作用:flow.org/try/…
    • 检查。是的,这行得通!不知道为什么我昨天尝试的时候它不起作用......谢谢!您能否将此示例添加到您的答案中以供未来读者参考?
    • > 要使其逆变,请使用 + 符号 这很令人困惑,文档说 + 用于协变:flow.org/en/docs/types/interfaces/…