此代码段中出现流错误
type MyType = 'aa' | 'bb'
const str = 'a'
const test1: MyType = `a${str}` // ERROR
const test2: MyType = 'a' + str // ERROR
错误
const test1: MyType = `a${str}` // ERROR
^ Cannot assign template string to `test1` because: Either string [1] is incompatible with string literal `aa` [2]. Or string [1] is incompatible with string literal `bb` [3].
References:
6: const test1: MyType = `a${str}` // ERROR
^ [1]
3: type MyType = 'aa' | 'bb'
^ [2]
3: type MyType = 'aa' | 'bb'
^ [3]
有人知道为什么流程不支持这一点吗?还是有更好的方法编写此代码以使流程愉快?谢谢!
答案 0 :(得分:2)
Flow是Javascript的静态类型检查器。这意味着流程仅以静态方式分析源代码。
在您的示例中,{strong}运行时的变量test1
和test2
都等于aa
,这等于{ {1}}。
不幸的是,这是运行时的结果。流仅检查静态规则。据我所知,Flow永远不会执行它检查的代码(即使代码像您的示例中那样琐碎)。
答案 1 :(得分:-1)
归因于wchargin
简短的回答:不要写
长答案:
以下是我想要此功能的原因:
// =====================
// Icon component
// =====================
// ICONS contains all the svg icons in the app
// I use prefix like FRUIT_ to differentiate the categories
const ICONS = {
FRUIT_APPLE: <svg>...</svg>,
FRUIT_ORANGE: <svg>...</svg>,
VEGE_BROCCOLI: <svg>...</svg>,
VEGE_TOMATO: <svg>...</svg>,
...
}
const Props = {
type: $Keys<typeof ICONS>
}
const Icon = ({ type }: Props): Element<*> => (
<i className={`Icon Icon--{type}`} />
)
// =====================
// FruitCard component
// =====================
type Props = {
fruitType: 'APPLE' | 'ORANGE',
foo: string,
...
}
const FruitCard = (props: Props) => (
<div>
<div>{props.foo}</div>
{/* Flow ERROR below */}
<Icon type={`FRUIT_${props.fruitType}`} />
</div>
)
解决方案1:使用对象将fruitType转换为图标类型 这是我正在使用的解决方案
// =====================
// FruitCard component
// =====================
type Props = {
fruitType: 'APPLE' | 'ORANGE',
foo: string,
...
}
// Use object to convert fruitType to icon type
const FRUIT_TYPE_TO_ICON_TYPE = {
APPLE: 'FRUIT_APPLE',
ORANGE: 'FRUIT_ORANGE',
}
const FruitCard = (props: Props) => (
<div>
<div>{props.foo}</div>
{/* This works in flow */}
<Icon type={FRUIT_TYPE_TO_ICON_TYPE[props.fruitType]} />
</div>
)
解决方案2:使用$ ObjMap,将层次结构嵌入类型系统 这归因于wchargin。我认为这是一个更好的解决方案,因为它不需要对象映射。
import React, {type Element} from "react";
// Type definitions. (There are other ways to structure these; this is
// relatively simple and works fine.)
type IconCategories = {
FRUIT: FruitTypes,
VEGE: VegeTypes,
};
type FruitTypes = {
APPLE: "APPLE",
ORANGE: "ORANGE",
};
type VegeTypes = {
BROCCOLI: "BROCCOLI",
TOMATO: "TOMATO",
};
// ICONS contains all the SVG icons in the app.
// Each category has its own nested object.
// This will fail to typecheck if you forget to include a category, or
// include an extra category, or forget to include a fruit/vegetable, or
// include an extra fruit/vegetable, or if any of the leaves is not an
// <svg /> element.
const ICONS: $Exact<$ObjMap<IconCategories, <T>(T) =>
$Exact<$ObjMap<T, <U>(U) => Element<"svg">>>
>> = {
FRUIT: {
APPLE: <svg />,
ORANGE: <svg />,
},
VEGE: {
BROCCOLI: <svg />,
TOMATO: <svg />,
},
};
// Icon component
// Example usage: <Icon category="FRUIT" type="APPLE" />.
const Icon = <T: $Keys<IconCategories>>(props: {
category: T,
type: $Keys<$ElementType<IconCategories, T>>,
}) => (
<i className={`Icon Icon--${props.category}_${props.type}`} />
);
// The following typecheck (which is good):
<Icon category="FRUIT" type="APPLE" />;
<Icon category="VEGE" type="BROCCOLI" />;
// The following do not typecheck (which is good):
/*
<Icon category="FRUIT" type="BROCCOLI" />;
<Icon category="VEGE" type="APPLE" />;
<Icon category="NOT_A_CATEGORY" type="WAT" />;
<Icon category="FRUIT" type="NOT_A_FRUIT" />;
*/
// FruitCard component
type Props = {
fruitType: "APPLE" | "ORANGE", // or: `fruitType: $Keys<FruitTypes>`
foo: string,
};
const FruitCard = (props: Props) => (
<div>
<div>{props.foo}</div>
{/* This typechecks, correctly */}
<Icon category="FRUIT" type={props.fruitType} />
</div>
);
这里是关于github的更深入的讨论: https://github.com/facebook/flow/issues/6789