Flow:不是多态类型
Flow: is not a polymorphic type
我刚刚第一次集成 flow 以静态检查我的 javascript 来源。
我正在努力解决错误流程发现问题,但我无法自行解决。它是关于使用 es6 classes 和继承。更具体地说,我创建了一些反应组件,它们应该继承一些方法。
我有一个标注组件,它表示未指定严重性的标注消息。为了使事情更简单一些,我考虑提供一个 ErrorMessage 组件,它继承了 Callout 组件。我的 classes 结构如下:
React.Component
> AbstractComponent (here i add some project-wide helpers for i18n and so on
> Callout (this represents a pretty message on the screen)
> ErrorMessage (this represents an error)
流量告诉我:
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/js/Components/Generic/ErrorMessage.js:14:43
statics of Callout [1] is not a polymorphic type.
11│ icon: string
12│ };
13│
[1] 14│ export default class ErrorMessage extends Callout<Props> {
15│
16│ static get defaultProps(): Props {
17│ return {
Callout<Props>
部分被突出显示
我已经为标注定义了道具类型 class,所以这可能是问题所在,但我无法自行解决。
下面几行抛出了类似的错误,我尝试通过寻址 super.content 访问父方法(内容是 Callout 的获取方法)。
提前致谢
更新:为什么我要使用 class 继承?
继承Callout > ErrorMessage只是为了减少冗余代码而存在的,但不是必须的,所以让我们忽略它并说一个更常见的情况:
我想要一个 class AbstractComponent
来简化我项目中的常见事情。
一些例子:
翻译字符串的打印:为了使组件多语言化,我创建了一个实用程序class来生成翻译字符串,在组件内部它的工作方式类似于
function render() {
return (
<div>
{new Translation(
'namespace',
'key',
[some, args],
`${some} fallback message with optional ${args}`
).toString()}
</div>
)
}
为了使用它,我堆栈中的每个组件最终都在顶部添加了 import 语句
import Translation from "../Core/Translation"
或者在最好的情况下
import t from "../Core/Translation"
我使用 webpack 构建一个包,而 webpack 似乎会在你使用的每个导入语句中破坏已编译的 javascript。所以我想 - 为了减少编码工作和包大小 - 我提供了一个中间组件 class,它添加了一些实用方法,例如:
class AbstractComponent extends React.Component {
constructor(props) {
super(props);
this.logger = props.logger || new Logger();
this.api: ApiInterface = props.api || new MockApi();
}
translate(namespace: string, key: string, args: ?[] = null, fallback: ?string): string {
return new Translation(namespace, key, args, fallback).toString();
}
svgSprite(id: string, className: string = "") {
return (
<SvgSprite id={id} className={className} />
)
}
}
我还添加了一些其他内容来向您展示更多使用中间组件的理由 class。
所以,所有这些都有效!但是流抱怨缺少 return 类型等等,这对我来说很好,为此我想使用流!我无法解决的问题是继承本身......但对我来说它确实很有意义。
如果你真的想处理继承(我对此没有意见,我只是觉得你以后可能 运行 会遇到问题),你可以这样做 something like the following:
class AbstractComponent<Props: {}, State: ?{} = null> extends React.Component<Props, State> {
api: ApiInterface
logger: typeof Logger
constructor(props) {
super(props);
this.logger = props.logger || new Logger();
this.api = props.api || new MockApi();
}
translate(namespace: string, key: string, args: ?string[] = null, fallback: ?string): string {
return new Translation(namespace, key, args, fallback).toString();
}
svgSprite(id: string, className: string = "") {
return (
<SvgSprite id={id} className={className} />
)
}
}
并像这样使用它:
class Test extends AbstractComponent<{ some: string, args: string }> {
render() {
const { some, args } = this.props
return (
<div>
{this.translate(
'namespace',
'key',
[some, args],
`${some} fallback message with optional ${args}`
)}
</div>
)
}
}
现在,我要说在某种程度上我理解 Facebook 的来源。在这种情况下,您的组件实际上已经是一个抽象构造。如果你想让它更灵活(假设你有一个无状态组件,可以从 logger
和 translate
函数中受益),你可以 one of two things:
这是我在两者中都使用的定义类型和翻译函数:
type CommonProps = {
logger?: Logger,
api?: ApiInterface,
translate?: (namespace: string, key: string, args: ?string[], fallback: ?string) => string
}
// This should look familiar
function translate(namespace: string, key: string, args: ?string[] = null, fallback: ?string): string {
return new Translation(namespace, key, args, fallback).toString();
}
高阶分量
function addCommonStuff({ logger = new Logger(), api = new MockApi(), translate = translate }: CommonProps) {
return <Props: {}>(
WrappedComponent: ComponentType<Props>
): ComponentType<
$Diff<Props, $NonMaybeType<CommonProps>>
> => (props: Props) => <WrappedComponent {...props} logger={logger} api={api} translate={translate} />
}
并像这样使用:
class Test extends React.Component<{}> {}
const TestWithCommons = addCommonStuff({})(Test)
;<TestWithCommons />
具有渲染属性的可重用组件
class Common extends React.Component<CommonProps & { render?: Function, children?: Function }, $NonMaybeType<CommonProps>> {
state = {
logger: this.props.logger || new Logger(),
api: this.props.api || new MockApi(),
translate: translate
}
render() {
const { children, render } = this.props
return typeof render === 'function' ? render(this.state) : (
typeof children === 'function' ? children(this.state) : null
)
}
}
并像这样使用它:
class TestCommon extends React.Component<{}> {
render() {
return <Common>
{({ logger, api, translate }) => translate('namespace',
'key',
null,
`Fallback message`
)}
</Common>
}
}
作为本次讨论的旁白,您不需要将 defaultProps
写成 getter 作为标注。 static defaultProps = {}
应该够了。它不应该考虑传入的道具或任何东西。如果是这样,你最好使用 state
我刚刚第一次集成 flow 以静态检查我的 javascript 来源。
我正在努力解决错误流程发现问题,但我无法自行解决。它是关于使用 es6 classes 和继承。更具体地说,我创建了一些反应组件,它们应该继承一些方法。
我有一个标注组件,它表示未指定严重性的标注消息。为了使事情更简单一些,我考虑提供一个 ErrorMessage 组件,它继承了 Callout 组件。我的 classes 结构如下:
React.Component
> AbstractComponent (here i add some project-wide helpers for i18n and so on
> Callout (this represents a pretty message on the screen)
> ErrorMessage (this represents an error)
流量告诉我:
Error ┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈ src/js/Components/Generic/ErrorMessage.js:14:43
statics of Callout [1] is not a polymorphic type.
11│ icon: string
12│ };
13│
[1] 14│ export default class ErrorMessage extends Callout<Props> {
15│
16│ static get defaultProps(): Props {
17│ return {
Callout<Props>
部分被突出显示
我已经为标注定义了道具类型 class,所以这可能是问题所在,但我无法自行解决。
下面几行抛出了类似的错误,我尝试通过寻址 super.content 访问父方法(内容是 Callout 的获取方法)。
提前致谢
更新:为什么我要使用 class 继承?
继承Callout > ErrorMessage只是为了减少冗余代码而存在的,但不是必须的,所以让我们忽略它并说一个更常见的情况:
我想要一个 class AbstractComponent
来简化我项目中的常见事情。
一些例子:
翻译字符串的打印:为了使组件多语言化,我创建了一个实用程序class来生成翻译字符串,在组件内部它的工作方式类似于
function render() {
return (
<div>
{new Translation(
'namespace',
'key',
[some, args],
`${some} fallback message with optional ${args}`
).toString()}
</div>
)
}
为了使用它,我堆栈中的每个组件最终都在顶部添加了 import 语句
import Translation from "../Core/Translation"
或者在最好的情况下
import t from "../Core/Translation"
我使用 webpack 构建一个包,而 webpack 似乎会在你使用的每个导入语句中破坏已编译的 javascript。所以我想 - 为了减少编码工作和包大小 - 我提供了一个中间组件 class,它添加了一些实用方法,例如:
class AbstractComponent extends React.Component {
constructor(props) {
super(props);
this.logger = props.logger || new Logger();
this.api: ApiInterface = props.api || new MockApi();
}
translate(namespace: string, key: string, args: ?[] = null, fallback: ?string): string {
return new Translation(namespace, key, args, fallback).toString();
}
svgSprite(id: string, className: string = "") {
return (
<SvgSprite id={id} className={className} />
)
}
}
我还添加了一些其他内容来向您展示更多使用中间组件的理由 class。
所以,所有这些都有效!但是流抱怨缺少 return 类型等等,这对我来说很好,为此我想使用流!我无法解决的问题是继承本身......但对我来说它确实很有意义。
如果你真的想处理继承(我对此没有意见,我只是觉得你以后可能 运行 会遇到问题),你可以这样做 something like the following:
class AbstractComponent<Props: {}, State: ?{} = null> extends React.Component<Props, State> {
api: ApiInterface
logger: typeof Logger
constructor(props) {
super(props);
this.logger = props.logger || new Logger();
this.api = props.api || new MockApi();
}
translate(namespace: string, key: string, args: ?string[] = null, fallback: ?string): string {
return new Translation(namespace, key, args, fallback).toString();
}
svgSprite(id: string, className: string = "") {
return (
<SvgSprite id={id} className={className} />
)
}
}
并像这样使用它:
class Test extends AbstractComponent<{ some: string, args: string }> {
render() {
const { some, args } = this.props
return (
<div>
{this.translate(
'namespace',
'key',
[some, args],
`${some} fallback message with optional ${args}`
)}
</div>
)
}
}
现在,我要说在某种程度上我理解 Facebook 的来源。在这种情况下,您的组件实际上已经是一个抽象构造。如果你想让它更灵活(假设你有一个无状态组件,可以从 logger
和 translate
函数中受益),你可以 one of two things:
这是我在两者中都使用的定义类型和翻译函数:
type CommonProps = {
logger?: Logger,
api?: ApiInterface,
translate?: (namespace: string, key: string, args: ?string[], fallback: ?string) => string
}
// This should look familiar
function translate(namespace: string, key: string, args: ?string[] = null, fallback: ?string): string {
return new Translation(namespace, key, args, fallback).toString();
}
高阶分量
function addCommonStuff({ logger = new Logger(), api = new MockApi(), translate = translate }: CommonProps) {
return <Props: {}>(
WrappedComponent: ComponentType<Props>
): ComponentType<
$Diff<Props, $NonMaybeType<CommonProps>>
> => (props: Props) => <WrappedComponent {...props} logger={logger} api={api} translate={translate} />
}
并像这样使用:
class Test extends React.Component<{}> {}
const TestWithCommons = addCommonStuff({})(Test)
;<TestWithCommons />
具有渲染属性的可重用组件
class Common extends React.Component<CommonProps & { render?: Function, children?: Function }, $NonMaybeType<CommonProps>> {
state = {
logger: this.props.logger || new Logger(),
api: this.props.api || new MockApi(),
translate: translate
}
render() {
const { children, render } = this.props
return typeof render === 'function' ? render(this.state) : (
typeof children === 'function' ? children(this.state) : null
)
}
}
并像这样使用它:
class TestCommon extends React.Component<{}> {
render() {
return <Common>
{({ logger, api, translate }) => translate('namespace',
'key',
null,
`Fallback message`
)}
</Common>
}
}
作为本次讨论的旁白,您不需要将 defaultProps
写成 getter 作为标注。 static defaultProps = {}
应该够了。它不应该考虑传入的道具或任何东西。如果是这样,你最好使用 state