如何强制 Flow 将一个值转换为另一种类型?
How can Flow be forced to cast a value to another type?
是否可以在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
Flow 不会直接从一种类型转换为另一种类型,但您可以执行类似
const bar: string = (foo: any);
所以你将 foo
转换为 any
,因为 any
接受任何类型的值作为输入。然后因为 any
类型还允许您从中读取所有可能的类型,所以您可以将 any
值分配给 bar
因为 any
也是 string
.
在您提供的示例中,您正在查看从联合类型到其成员之一的 "cast"。虽然通常将其视为强制转换,但它与其他语言中的类型转换不同。
通过将 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会在this的第二行报错,报unionValue可能是'number':
类型
const unionValue: StringOrNumber = 'seven'
const stringValue: string = unionValue
但是,通过使用抑制评论,这通过了流程:
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 将报告 "Unused suppression" 错误,提醒我们。这在 Flow 应该能够识别细化但不能识别时特别有用——通过使用抑制注释,如果 Flow 的未来版本将代码识别为类型安全。
任意施法
如果你真的不能用一种能证明其流动安全性的方式来表达它,并且你不能(或不会)使用抑制注释,你可以将任何类型转换为 any
, 和 any
到任何类型:
const unionValue: StringOrNumber = 'seven'
// Flow will be okay with this:
const stringValue: string = (unionValue: any)
通过将一个值转换为 any
,我们要求 Flow 忘记它所知道的关于值类型的任何信息,并假设我们用它做的任何事情都必须是正确的。如果我们稍后将其放入类型化变量中,Flow 将假定它一定是正确的。
注意事项
重要的是要注意抑制注释和强制转换都是 不安全。他们完全覆盖 Flow,并且会愉快地执行完全无意义的 "casts":
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
持有来自 notAString
的 object
,但 Flow 确定它是一个字符串。
为避免这种情况,尽可能使用 Flow 理解的改进,并避免使用其他不安全的 "casting" 技术。
这个答案只是一个建议。在浏览 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));
也许是对一个或多个现有答案的更新:目前有一种相当不错的方法可以专门告诉 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-specific 语法修饰代码的方法,同时又不会阻止它也像纯 JavaScript 一样运行——这具有允许在不使用 Flow 的情况下使用的适度好处任何 Flow-syntax-stripping 工具,如 Babel 或其他工具。但该功能也可以很好地用作表达只有 Flow 可以读取的“断言”的机制。这有点 hacky,但我个人认为它足够干净,可以用于一个严肃的项目!我的意思是,只要你只是偶尔做一次...
当然,您可以将 typeof
检查留在实际 JavaScript 中,这实际上会 运行,但我想这可能会有 运行 时间成本?而这个,肯定不会。
是否可以在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
Flow 不会直接从一种类型转换为另一种类型,但您可以执行类似
const bar: string = (foo: any);
所以你将 foo
转换为 any
,因为 any
接受任何类型的值作为输入。然后因为 any
类型还允许您从中读取所有可能的类型,所以您可以将 any
值分配给 bar
因为 any
也是 string
.
在您提供的示例中,您正在查看从联合类型到其成员之一的 "cast"。虽然通常将其视为强制转换,但它与其他语言中的类型转换不同。
通过将 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会在this的第二行报错,报unionValue可能是'number':
类型const unionValue: StringOrNumber = 'seven'
const stringValue: string = unionValue
但是,通过使用抑制评论,这通过了流程:
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 将报告 "Unused suppression" 错误,提醒我们。这在 Flow 应该能够识别细化但不能识别时特别有用——通过使用抑制注释,如果 Flow 的未来版本将代码识别为类型安全。
任意施法
如果你真的不能用一种能证明其流动安全性的方式来表达它,并且你不能(或不会)使用抑制注释,你可以将任何类型转换为 any
, 和 any
到任何类型:
const unionValue: StringOrNumber = 'seven'
// Flow will be okay with this:
const stringValue: string = (unionValue: any)
通过将一个值转换为 any
,我们要求 Flow 忘记它所知道的关于值类型的任何信息,并假设我们用它做的任何事情都必须是正确的。如果我们稍后将其放入类型化变量中,Flow 将假定它一定是正确的。
注意事项
重要的是要注意抑制注释和强制转换都是 不安全。他们完全覆盖 Flow,并且会愉快地执行完全无意义的 "casts":
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
持有来自 notAString
的 object
,但 Flow 确定它是一个字符串。
为避免这种情况,尽可能使用 Flow 理解的改进,并避免使用其他不安全的 "casting" 技术。
这个答案只是一个建议。在浏览 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));
也许是对一个或多个现有答案的更新:目前有一种相当不错的方法可以专门告诉 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-specific 语法修饰代码的方法,同时又不会阻止它也像纯 JavaScript 一样运行——这具有允许在不使用 Flow 的情况下使用的适度好处任何 Flow-syntax-stripping 工具,如 Babel 或其他工具。但该功能也可以很好地用作表达只有 Flow 可以读取的“断言”的机制。这有点 hacky,但我个人认为它足够干净,可以用于一个严肃的项目!我的意思是,只要你只是偶尔做一次...
当然,您可以将 typeof
检查留在实际 JavaScript 中,这实际上会 运行,但我想这可能会有 运行 时间成本?而这个,肯定不会。