是否可以在Flow中强制转换变量?
type StringOrNumber = string | number
const foo: StringOrNumber = 'hello'
// I look for something like `const bar:string = (string) foo`
const bar: string = foo // fails
const bar: string = (foo: string) // also fails
答案 0 :(得分:36)
Flow不会直接从一种类型转换为另一种类型,但您可以执行类似
的操作const bar: string = (foo: any);
所以你将foo
投射到any
,因为any
接受任何类型的值作为输入。然后,由于any
类型还允许您从中读取所有可能的类型,因此您可以将any
值分配给bar
,因为any
也是string
}。
答案 1 :(得分:17)
在示例中,您将看到从联合类型到其成员之一的“强制转换”。虽然将此视为演员表是常见的,但它与其他语言中的类型转换不同。
通过将foo
的类型设置为string | number
,我们告诉Flow,此值可以是字符串或数字。然后我们碰巧在其中添加了一个字符串,但Flow并没有放弃我们关于它的类型的直接断言,因为即使在这种情况下(比如这个)以后它也无法改变。
要将其分配给string
类型的变量,Flow需要知道即使它可能是string
或number
,当我们执行分配时确保它只能是string
。
减少可能选项的过程称为类型细化。
要优化类型,我们需要证明它必须是我们所说的类型,以Flow理解的方式。
在原始示例中,您可以使用typeof
执行此操作:
type StringOrNumber = string | number
const foo: StringOrNumber = 'hello'
// This would raise an error here:
// const bar: string = foo
if (typeof foo === "string") {
// Flow now knows that foo must be a string, and allows this.
const bar: string = foo
}
Flow并不理解人类可以看到的所有类型细化,因此有时您需要查看the refinement docs以查看Flow可以理解的内容。
有时候没有办法表达对Flow的改进的安全性。我们可以通过使用suppression comment强制Flow接受一个语句,这将抑制Flow否则会报告的错误。默认抑制注释为$FlowFixMe
,但可以将其配置为不同的注释或注释。
Flow会在第二行报告错误,报告unionValue可能是'number'类型:
const unionValue: StringOrNumber = 'seven'
const stringValue: string = unionValue
但是,通过使用抑制注释,这会传递Flow:
const unionValue: StringOrNumber = 'seven'
// $FlowFixMe: We can plainly see this is a string!
const stringValue: string = unionValue
抑制注释的一个有用功能是抑制注释没有跟随错误被抑制被认为是错误。如果我们更改示例中的类型:
const unionValue: string = 'seven'
// $FlowFixMe: Even though this is a string, suppress it
const stringValue: string = unionValue
现在Flow将报告“未使用的抑制”错误,提醒我们。当Flow应该能够识别细化但不能通过使用抑制注释时,如果Flow的未来版本将代码识别为已删除评论(并获得额外的类型安全性),则此功能特别有用类型安全的。
如果你真的无法以证明其安全性的方式表达它,并且你不能(或不会)使用抑制注释,你可以将任何类型转换为any
,并且any
到任何类型:
const unionValue: StringOrNumber = 'seven'
// Flow will be okay with this:
const stringValue: string = (unionValue: any)
通过将值转换为any
,我们要求Flow忘记它所知道的值的类型,并假设我们正在做的事情必须是正确的。如果我们稍后将它放入一个类型变量中,Flow将认为必须是正确的。
重要的是要注意抑制注释和强制转换都是不安全。它们完全覆盖了Flow,并且很乐意执行完全无意义的“强制转换”:
const notAString: {key: string, key2: number} = {key: 'value', key2: 123}
// This isn't right, but Flow won't complain:
const stringValue: string = (notAString: any)
在此示例中,stringValue
持有object
notAString
,但Flow确定它是一个字符串。
为避免这种情况,尽可能使用Flow理解的优化,并避免使用其他不安全的“投射”技术。
答案 2 :(得分:1)
这个答案只是一个建议。在浏览Event和HTMLElement相关类型检查问题的解决方案时,我遇到了许多调用instanceof的守卫。
为了满足类型检查,我刚刚介绍了这个通用防护并将其称为cast
(当然不会使它成为演员),因为否则我的代码会变得臃肿。
成本当然是性能(在编写游戏时非常相关,但我猜大多数用例从类型保护中获益更多,而不是每次迭代毫秒)。
const cast = (type : any, target : any) => {
if (!(target instanceof type)) {
throw new Error(`${target} is not a ${type}`);
}
return target;
}
用途:
const fooLayer = cast(HTMLCanvasElement, document.getElementById("foo-layer"));
window.addEventListener("click", (ev : Event) =>
console.log(cast(MouseEvent, ev).clientX - cast(HTMLElement, ev.target).offsetLeft,
cast(MouseEvent, ev).clientY - cast(HTMLElement, ev.target).offsetTop));
答案 3 :(得分:0)
也许是对一个或多个现有答案的更新:当前有一种相当不错的方法专门告诉Flow不要担心它认为是不好的演员。
您可以提供带有特定错误代码的错误抑制指令,如下所示:
type StringOrNumber = string | number
const foo: StringOrNumber = 'hello'
// I look for something like `const bar:string = (string) foo`
// const bar: string = foo // would fail
// $FlowExpectedError[incompatible-cast]
const baz: string = (foo: string) // no longer fails!!
作为额外的奖励,这是在没有Flow的任何投诉的情况下执行演员表的另一种方法:
/*:: if (typeof foo !== "string") throw null; */
const baz: string = (foo: string) // does not fail!!
这里特殊的/*::
语法告诉Flow,它应该将注释内的文本(冒号除外)当作普通的JavaScript代码一样对待。我从文档中收集到该功能的存在,以提供一种使用特定于Flow的语法修饰代码而又不阻止它也充当纯JavaScript的方法-这具有适度的好处,即允许使用Flow而不使用任何Flow-syntax-stripping像Babel之类的工具。但是,该功能还可以很好地用作表达只有Flow才能读取的“断言”的机制。它有点笨拙,但我个人认为它足够干净,可以在严肃的项目中使用!我的意思是,只要您偶尔进行一次...
当然,您可以将typeof
留在将实际运行的实际JavaScript中,但是我想这可能会增加运行时成本?另一方面,这肯定不会。