sophister / 2bugua5

工作、生活的碎碎念
12 stars 3 forks source link

TypeScript相关 #17

Open sophister opened 5 years ago

sophister commented 5 years ago

WebStorm 中使用 ESlint 校验 typescript ts/tsx

备注:我使用的 webstorm版本是:

WebStorm 2018.3.5
Build #WS-183.5912.23, built on February 27, 2019

webstorm 中,即使打开了IDE自带的 eslint校验,即使eslint能找到我们对应的 .eslintrc文件,依然很多配置的规则没有被应用到 ts/tsx文件的校验,比如我们的 .eslintrc 文件内容如下:

{
  "extends": [
    "airbnb",
    "plugin:@typescript-eslint/recommended"
  ],
  "parser": "@typescript-eslint/parser",
  "ecmaFeatures": {
    "classes": true
  },
  "plugins": [
    "react-hooks",
    "@typescript-eslint"
  ],
  "rules": {
    "react/jsx-filename-extension": ["error", { "extensions": [".js", ".jsx", ".ts", ".tsx"] }],
    "react/jsx-indent":["error",4],
    "global-require": "off",
    "indent": ["error", 4,{ "SwitchCase": 1 }],
    "no-unused-expressions":0,           
    "class-methods-use-this":0,           
    "no-underscore-dangle":0,            
    "arrow-body-style":["error", "always"], 
    "max-len":["error",200],
    "no-plusplus":["error",{"allowForLoopAfterthoughts": true}],
    "prefer-destructuring": 0,
    "guard-for-in":"off",
    "react/jsx-indent-props":["error",4],
    "react/require-default-props": 0,     
    "react/no-array-index-key":0,
    "react/jsx-boolean-value":["error","always"],
    "react/forbid-prop-types":0,
    "react/prop-types":0,
    "react/sort-comp":0,
    "react/destructuring-assignment":0,
    "object-curly-newline": ["error", {
        "ObjectExpression": { "consistent": true,"minProperties": 5 },
        "ObjectPattern": { "consistent": true, "minProperties": 5},
        "ImportDeclaration": { "consistent": true,  "minProperties": 5 },
        "ExportDeclaration": { "consistent": true, "minProperties": 5 }
    }],
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
    "import/no-extraneous-dependencies": [
        "error", {"packageDir": "./"}
    ],
    "react/jsx-one-expression-per-line":  0,
    "import/prefer-default-export": 0,
    "no-shadow": 0,
    "@typescript-eslint/explicit-function-return-type": ["error", {
        "allowExpressions": true,
        "allowTypedFunctionExpressions": true
    }],
    "@typescript-eslint/camelcase": 0,
    "@typescript-eslint/no-non-null-assertion": 0
  },
  "globals": {
    "requestAnimationFrame": true,
    "cancelAnimationFrame": true,
    "isNaN": true
  },
  "settings": {
    "import/core-modules": ["common","components"],
    "import/resolver": {
      "node": {
        "moduleDirectory": [
          "./node_modules/",
          "./src/"
        ],
        "extensions": [
            ".js",
            ".jsx",
            ".ts",
            ".tsx"
        ]
      }
    },
    "import/parsers": {
      "@typescript-eslint/parser": [".ts", ".tsx", ".js", ".jsx"]
    }
  }
}

还需要单独在 webstorm 里配置一下,默认 eslint 不会去处理 .ts/.tsx的文件,具体设置见这个帖子:https://intellij-support.jetbrains.com/hc/en-us/community/posts/115000225170/comments/360000332879

为了防止打不开原文,截图保留如下:

image

内容如下:

To anyone else searching for this, you must go to Help > Find Action and type "registry". Click the first result (Registry...) and scroll down to eslint.additional.file.extensions. Add js,ts.

The issue for me is that IntelliJ/Webstorm only does this automatically if you're using typescript-eslint-parser and eslint-plugin-typescript. However, those packages are now @typescript-eslint/parser and @typescript-eslint/eslint-plugin.

image

sophister commented 5 years ago

WebStorm 中JS/TS混用导致eslint找不到import的文件

已经配置了 import/resolverextensions 字段,webstorm里还是报错,大概如下:

image

参考这个issue: https://github.com/kriasoft/react-starter-kit/issues/1180#issuecomment-447109110 ,还需要加上额外的 path 字段,最终的 .eslintrc 文件如下:

{
  "extends": [
    "airbnb",
    "plugin:@typescript-eslint/recommended"
  ],
  "parser": "@typescript-eslint/parser",
  "ecmaFeatures": {
    "classes": true
  },
  "plugins": [
    "react-hooks",
    "@typescript-eslint"
  ],
  "rules": {
    "react/jsx-filename-extension": ["error", { "extensions": [".js", ".jsx", ".ts", ".tsx"] }],
    "react/jsx-indent":["error",4],
    "global-require": "off",
    "indent": ["error", 4,{ "SwitchCase": 1 }],
    "no-unused-expressions":0,
    "class-methods-use-this":0,
    "no-underscore-dangle":0,
    "arrow-body-style":["error", "always"],
    "max-len":["error",200],
    "no-plusplus":["error",{"allowForLoopAfterthoughts": true}],
    "prefer-destructuring": 0,
    "guard-for-in":"off",
    "react/jsx-indent-props":["error",4],
    "react/require-default-props": 0,
    "react/no-array-index-key":0,
    "react/jsx-boolean-value":["error","always"],
    "react/forbid-prop-types":0,
    "react/prop-types":0,
    "react/sort-comp":0,
    "react/destructuring-assignment":0,
    "object-curly-newline": ["error", {
        "ObjectExpression": { "consistent": true,"minProperties": 5 },
        "ObjectPattern": { "consistent": true, "minProperties": 5},
        "ImportDeclaration": { "consistent": true,  "minProperties": 5 },
        "ExportDeclaration": { "consistent": true, "minProperties": 5 }
    }],
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
    "import/no-extraneous-dependencies": [
        "error", {"packageDir": "./"}
    ],
    "react/jsx-one-expression-per-line":  0,
    "import/prefer-default-export": 0,
    "no-shadow": 0,
    "@typescript-eslint/explicit-function-return-type": [0, {
        "allowExpressions": true,
        "allowTypedFunctionExpressions": true
    }],
    "@typescript-eslint/camelcase": 0,
    "@typescript-eslint/no-non-null-assertion": 0,
    "@typescript-eslint/explicit-member-accessibility": 0,
    "@typescript-eslint/no-object-literal-type-assertion": 0
  },
  "globals": {
    "requestAnimationFrame": true,
    "cancelAnimationFrame": true,
    "isNaN": true
  },
  "settings": {
    "import/core-modules": ["common","components"],
    "import/resolver": {
      "node": {
        "path": ["./src/"],
        "moduleDirectory": [
          "./node_modules/",
          "./src/"
        ],
        "extensions": [
            ".js",
            ".jsx",
            ".ts",
            ".tsx"
        ]
      }
    },
    "import/parsers": {
      "@typescript-eslint/parser": [".ts", ".tsx", ".js", ".jsx"]
    }
  }
}
sophister commented 5 years ago

TypeScript React.js defaultProps 定义怎么写?

  1. 根据 https://medium.com/@martin_hotell/10-typescript-pro-tips-patterns-with-or-without-react-5799488d6680 这篇文章的建议,写法如下:
const initialState = Object.freeze({ count: 0 });
const defaultProps = Object.freeze({ who: "jess" });

type State = typeof initialState;
type DefaultProps = typeof defaultProps;

type Props = { someProps: string } & DefaultProps;

export default class Counter extends Component<Props, State> {
  static readonly defaultProps: DefaultProps = defaultProps;
  readonly state: State = initialState;

  render() {
    return (
      <div>
        <div>count from state is: {this.state.count}</div>
        <div>
          porps:
          <div>who {this.props.who}</div>
          <div>someProps {this.props.someProps}</div>
        </div>
      </div>
    );
  }
}
  1. 发现TS官方已经支持了 defaultProps 的定义,在这里 https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#support-for-defaultprops-in-jsx但是,貌似有些bug,写法如下:
export interface Props {
    name: string;
}

export class Greet extends React.Component<Props> {
    render() {
        const { name } = this.props;
        return <div>Hello ${name.toUpperCase()}!</div>;
    }
    static defaultProps = { name: "world"};
}

// Type-checks! No type assertions needed!
let el = <Greet />

TS官方的写法,function component 不能使用 FC<Props>,使用之后, defaultProps 不能识别出来,还真有这个问题 ,上面那篇文章里提到了。

image

type Props = {
  who: string
} & typeof defaultProps

const defaultProps = {
  greeting: 'Hello',
}

const Greeter: FC<Props> = (props) => (
  <div>
    {props.greeting} {props.who}!
  </div>
)

// 🚨 This won't work. 
// Greeter components API will not mark `greeting` as optional
Greeter.defaultProps = defaultProps

const Test = () => (
  <>
    {/**
      ExpectError ❌
      Property 'greeting' is missing
    */}
    <Greeter who="Martin" />
  </>
)
sophister commented 4 years ago

TypeScript 变量类型确定之后不能修改

TS 里,一个变量的类型确定之后,就 不能 修改了,除非这个变量声明的类型是 Union 类型。

但是,即使是对某个 Union 类型的变量进行类型转换,也有需要注意的地方,比如下面两个例子:

下面这个例子没问题:

interface Item {
    name: string
}

interface WithFoo {
    foo?: string;
}

function addProp(obj: Item | WithFoo) {
    // 类型转换之后,obj 的类型缩小为 WithFoo
    obj = obj as WithFoo;
    obj.foo = 'hello';
}

下面这个例子,会报错:

interface Item {
    name: string
}

interface WithFoo {
    name: string;
    foo?: string;
}

function addProp(obj: Item | WithFoo) {
    // 经过下面的转换之后,obj 的类型 **仍然** 是 Item | WithFoo 
    obj = obj as WithFoo;
    // 下面这行会报错!
    obj.foo = 'hello';
}

根据 control flow analysis,通过赋值来改变变量的类型,按照如下方式处理:

An assignment (including an initializer in a declaration) of a value of type S to a variable of type T changes the type of that variable to T narrowed by S in the code path that follows the assignment.

The type T narrowed by S is computed as follows:

If T is not a union type, the result is T. If T is a union type, the result is the union of each constituent type in T to which S is assignable.

针对 T = S 的类型赋值:

根据条件1,如果 = 左边的类型T 不是 Union类型,赋值之后左侧变量的类型T保持不变

= 左边变量类型T Union 类型时,赋值之后的类型,是T中所有的 属于S子集的类型 Union

可以参考 https://stackoverflow.com/questions/58392378/typescript-type-cast