【问题标题】:TypeScript allows a function as React prop that conflicts with in-param of function signature?TypeScript 允许一个函数作为与函数签名的参数冲突的 React 道具?
【发布时间】:2018-10-04 22:33:49
【问题描述】:

为什么 TypeScript 类型检查器允许带有与定义不严格匹配的函数参数的 prop?

具体来说,我定义了一个函数callbackImpl = (str: string): number,并将其作为定义为callback(parameter: string | undefined): number; 的React prop 参数提供,这出乎意料地有效。

这对我来说是不直观的,在我看来非常危险!

但是!调用callbackImpl(undefined) 是行不通的,我认为这是正确的。

一个完整的例子:

import React from "react";

interface Props {
    callback(parameter: string | undefined): number;
}

class A extends React.Component<Props> {
    componentDidUpdate() {
        this.props.callback(undefined);
    }
}

class B extends React.Component {

    private callbackImpl = (str: string): number => {
        // Will crash if str is undefined
        return str.length;
    };

    // THIS IS NOT ALLOWED! And rightly so!
    private callLocalWithUndefined() {
        // TS2345: Argument of type 'undefined' is not assignable to parameter of type 'string'.
        this.callbackImpl(undefined);
    }

    render() {
        return (
            <React.Fragment>
                <A
                    // This is obviously just as illegal as what happens in callLocalWithUndefined,
                    // since callbackImpl explicitly does not accept undefined as the first parameter,
                    // but no type errors here!?
                    callback={this.callbackImpl}
                />
            </React.Fragment>
        );
    }
}

我在tsconfig.json中设置了"strict": true,

这是一个更完整的tsconfig.json 列表,省略了一些本地内容。

{
  "compilerOptions": {
    "outDir": "./dist/",
    "sourceMap": true,
    "noImplicitAny": true,
    "moduleResolution": "node",
    "module": "esnext",
    "allowSyntheticDefaultImports": true,
    "target": "es6",
    "jsx": "react",
    "allowJs": true,
    "strict": true,
    "noEmitOnError": true,
    "plugins": [],
    "baseUrl": "./",
    "paths": {
      // Omitted
    },
    "lib": [
      "es2017", // Object.entries support
      "dom"
    ],
    "types": ["gapi", "gapi.auth2", "node"]
  },
  "exclude": [
    "node_modules"
  ]
}

我做错了吗?我的 tsconfig 设置错了吗?我是不是误会了什么?

谢谢!

编辑

Titian Cernicova-Dragomir 回答后的其他资源

  • Strict Function Types 如 TS 2.6 发行说明所述。描述严格函数类型检查未涵盖的方法。

【问题讨论】:

  • 我更新了答案以反映添加的代码。

标签: reactjs typescript


【解决方案1】:

你是对的,这是不安全的。允许这种行为以简化从 JS 到 TS 的迁移是有理由的。好消息是,您可以使用 strictFunctionTypes 选择不允许这样做。

例如这段代码

declare const callbackImpl: (str: string) => number

let callback: (parameter: string | undefined) => number;

callback = callbackImpl

callback(undefined);

上面的代码使用strictNullChecks 编译,即使它不是完全类型安全的,正如您所注意到的。但同时使用strictNullChecksstrictFunctionTypes 将无法编译

注意我假设您已经在使用strictNullChecks,如果您没有使用,那么代码将按预期工作,因为没有此选项string|undefined 定义为string

编辑

上述通用解释是在问题包含实际代码之前发布的。编译器未捕获错误的原因是您将回调定义为方法。如果将其定义为函数字段,编译器会捕获错误。 我仍然不确定为什么见下文:

interface Props {
    callback: (parameter: string | undefined) => number;
}

class A extends React.Component<Props> {
    componentDidUpdate() {
        this.props.callback(undefined);
    }
}

class B extends React.Component {

    private callbackImpl = (str: string): number => {
        // Will crash if str is undefined
        return str.length;
    };


    render() {
        return (
            <React.Fragment>
                <A
                  // Error now
                    callback={this.callbackImpl}
                />
            </React.Fragment>
        );
    }
}

编辑

此行为是设计使然。 -strictFunctionTypes 标志不适用于原始 PR 中所述的方法

更严格的检查适用于所有函数类型,但源自方法或构造函数声明的函数类型除外。专门排除方法以确保泛型类和接口(例如 Array)继续主要以协变方式相关。严格检查方法的影响将是一个更大的突破性变化,因为大量泛型类型将变得不变(即便如此,我们可能会继续探索这种更严格的模式)。

【讨论】:

  • 感谢您的更新。如果您知道为什么它适用于函数字段而不适用于方法,我会非常高兴 =)
  • 谢谢!这是一个非常警告 - 不直观且危险。我个人已经等不及 WebAssembly 让我们摆脱荒谬的 JavaScript 遗留问题了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-18
  • 2016-05-03
相关资源
最近更新 更多