无法导入生成的类型文件 create-react-app

Can't import generated typings file create-react-app

这是我第一次创建 typescript react npm 模块,我正在尝试在我的项目中导入一个使用新 npm 模块的类型定义。 VSCode 智能感知能够找到并建议自动生成的 .d.ts 文件之一,但应用程序无法加载它。怎么回事?

./src/components/MyPage.tsx
Module not found: Can't resolve '@myorg/component-library/build/Input/Input.types' in '/Users/jbaczuk/path/to/project/src/components'

如果我将导入路径更改为 '@myorg/component-library/build/Input/Input.types.d',它会抛出:

./node_modules/@myorg/component-library/build/Input/Input.types.d.ts 2:7
Module parse failed: Unexpected token (2:7)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> export declare enum Type {
|     text = "text",
|     number = "number"

Input.types.d.ts

export declare enum Type {
    text = "text",
    number = "number"
}
export interface InputProps {
    type?: Type;
}
//# sourceMappingURL=Input.types.d.ts.map

MyPage.ts

import React from 'react';
import { Type } from '@myorg/component-library/build/Input/Input.types.d';

export default function MyPage(): JSX.Element {

{/** ... more stuff **/}}

return (
{/** ... more stuff **/}}
  <Input
    onChangeValue={onChangeText}
    type={Type.text}
  />
{/** ... more stuff **/}}
)

package.json

{
  "name": "my-project",
  "version": "0.1.0",
  "description": "My Project",
  "private": true,
  "repository": "git@github.com:MyOrg/MyProject.git",
  "license": "UNLICENSED",
  "dependencies": {
    "@myorg/component-library": "../../component-library",
    "axios": "^0.21.1",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-router-dom": "^5.2.0",
    "react-scripts": "4.0.3",
    "styled-components": "^5.2.1",
    "web-vitals": "^1.0.1"
  },
  "scripts": {
    "build:tailwind": "tailwind build src/tailwind.css -o src/tailwind.output.css",
    "watch:tailwind": "chokidar 'src/**/*.css' 'src/**/*.scss' --ignore src/tailwind.output.css -c 'npm run build:tailwind'",
    "start": "npm-run-all build:tailwind --parallel watch:tailwind start:react",
    "start:react": "PORT=3002 react-scripts start",
    "prebuild": "run-s build:tailwind",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "lint": "eslint '*/**/*.{js,ts,tsx}' --quiet --fix",
    "check-types": "tsc"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "@tailwindcss/postcss7-compat": "^2.0.4",
    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "@types/jest": "^26.0.15",
    "@types/node": "^12.0.0",
    "@types/react": "^17.0.0",
    "@types/react-dom": "^17.0.0",
    "@types/react-router-dom": "^5.1.7",
    "@types/testing-library__jest-dom": "^5.9.5",
    "@types/testing-library__react": "^10.2.0",
    "@typescript-eslint/eslint-plugin": "^4.14.2",
    "@typescript-eslint/parser": "^4.14.2",
    "autoprefixer": "^9",
    "chokidar-cli": "^2.1.0",
    "eslint": "^7.19.0",
    "eslint-config-prettier": "^7.2.0",
    "eslint-plugin-prettier": "^3.3.1",
    "eslint-plugin-react": "^7.22.0",
    "eslint-plugin-react-hooks": "^4.2.0",
    "husky": "^4.3.8",
    "lint-staged": ">=10",
    "npm-run-all": "^4.1.5",
    "postcss": "^7",
    "prettier": "^2.2.1",
    "react-scripts": "4.0.2",
    "tailwindcss": "npm:@tailwindcss/postcss7-compat",
    "ts-jest": "^26.5.0",
    "typescript": "^4.1.2"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
  "lint-staged": {
    "*.{js,ts,tsx}": [
      "eslint --fix"
    ]
  }
}

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext"
    ],
    "module": "esnext",
    "allowJs": true,
    "jsx": "react-jsx",
    "outDir": "./build",
    "noEmit": true,
    "isolatedModules": true,
    "strict": true,
    "noFallthroughCasesInSwitch": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "types": [
      "node",
      "jest"
    ],
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": [
    "src"
  ],
  "exclude": [
    "build"
  ]
}

组件库

package.json

{
  "name": "@myorg/component-library",
  "repository": {
    "type": "git",
    "url": "https://github.com/myOrg/ComponentLibrary.git"
  },
  "version": "0.2.1",
  "description": "MyOrg react component library",
  "main": "build/index.js",
  "module": "build/index.esm.js",
  "files": [
    "build"
  ],
  "types": "build/index.d.ts",
  "scripts": {
    "build": "rm -rf build && rollup -c --environment NODE_ENV:production",
    "test": "jest",
    "test:watch": "jest --watch",
    "lint": "eslint . --ext .ts",
    "lint:fix": "eslint . --fix --ext .ts",
    "storybook": "start-storybook -p 6006",
    "storybook:export": "build-storybook",
    "prepublishOnly": "npm run build",
    "create": "node ./util/create"
  },
  "author": "Jordan Baczuk",
  "license": "UNLICENSED",
  "devDependencies": {
    "@babel/core": "^7.13.14",
    "@rollup/plugin-commonjs": "^18.0.0",
    "@rollup/plugin-node-resolve": "^11.2.1",
    "@storybook/addon-essentials": "^6.2.2",
    "@storybook/addons": "^6.2.2",
    "@storybook/react": "^6.2.2",
    "@tailwindcss/postcss7-compat": "^2.0.4",
    "@testing-library/jest-dom": "^5.11.10",
    "@testing-library/react": "^11.2.6",
    "@testing-library/user-event": "^13.1.1",
    "@types/jest": "^26.0.22",
    "@types/react": "^17.0.3",
    "@typescript-eslint/eslint-plugin": "^4.14.0",
    "@typescript-eslint/parser": "^4.14.0",
    "autoprefixer": "^9",
    "babel-loader": "^8.2.2",
    "babel-preset-react-app": "^10.0.0",
    "eslint": "^7.18.0",
    "eslint-config-prettier": "^7.2.0",
    "eslint-plugin-prettier": "^3.3.1",
    "eslint-plugin-react": "^7.22.0",
    "eslint-plugin-react-hooks": "^4.2.0",
    "husky": "^4.3.8",
    "identity-obj-proxy": "^3.0.0",
    "jest": "^26.6.3",
    "lint-staged": ">=10",
    "postcss": "^7",
    "prettier": "^2.2.1",
    "rollup": "^2.44.0",
    "rollup-plugin-peer-deps-external": "^2.2.4",
    "rollup-plugin-postcss": "3.1.8",
    "rollup-plugin-typescript2": "^0.30.0",
    "tailwindcss": "npm:@tailwindcss/postcss7-compat",
    "ts-jest": "^26.5.4",
    "typescript": "^4.2.3"
  },
  "peerDependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2"
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged && npm test"
    }
  },
  "lint-staged": {
    "*.{js,ts,tsx}": [
      "eslint --fix"
    ],
    "*.js": "eslint --cache --fix"
  },
  "dependencies": {
    "@fontsource/titillium-web": "^4.2.2",
    "@heroicons/react": "^1.0.0",
    "react-tooltip": "^4.2.17"
  }
}

tsconfig.js

{
  "compilerOptions": {
    "declarationMap": true,
    "declaration": true,
    "declarationDir": "build",
    "module": "esnext",
    "target": "es5",
    "lib": ["es6", "dom", "es2016", "es2017"],
    "sourceMap": true,
    "jsx": "react",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true
  },
  "include": ["src/**/*"],
  "exclude": [
    "node_modules",
    "build",
    "src/**/*.stories.tsx",
    "src/**/*.spec.ts",
    "src/**/*.tests.tsx"
  ]
}

rollup.config.js

import peerDepsExternal from 'rollup-plugin-peer-deps-external'
import resolve from '@rollup/plugin-node-resolve'
import commonjs from '@rollup/plugin-commonjs'
import typescript from 'rollup-plugin-typescript2'
import packageJson from './package.json'
import postcss from 'rollup-plugin-postcss'

export default {
  input: 'src/index.ts',
  output: [
    {
      file: packageJson.main,
      format: 'cjs',
      sourcemap: true
    },
    {
      file: packageJson.module,
      format: 'esm',
      sourcemap: true
    }
  ],
  external: ['styled-components'],
  plugins: [
    peerDepsExternal(),
    postcss({
      minimize: true,
      modules: true,
      use: {
        sass: null,
        stylus: null,
        less: { javascriptEnabled: true }
      },
      extract: true
    }),
    resolve(),
    commonjs(),
    typescript()
  ]
}

我有点惊讶您直接从 types.d 文件加载类型并使用构建路径!我希望 import { Type } from '@myorg/component-library.

那是因为在为打字稿创作的 npm 库创建 package.json 的过程中,您将定义一个主 属性 指向导出所有 Javascript 属性的地方和一个类型 属性 指向导出你想要的打字稿类型的地方。

如果那个主要入口点(因为你的构建过程)实际上是 build/index.js 并且相应的类型文件是 build/index.d.ts 那么你在导入时永远不会直接引用构建文件夹或文件 -指向正确的路径由捆绑过程处理。

看看主流(但简单)的打字稿 npm 模块,如 https://github.com/jamiebuilds/unstated-next/blob/master/package.json ( https://www.npmjs.com/package/unstated-next )

您可以看到主要 属性 指向应该导出 javascript 名称的文件 https://github.com/jamiebuilds/unstated-next/blob/master/package.json#L6 and the types property pointing to the file which should export the types https://github.com/jamiebuilds/unstated-next/blob/master/package.json#L9 and these happen to be in the 'dist' folder but this is never referenced when importing it as per the docs at https://www.npmjs.com/package/unstated-next

我终于开始工作了,并且学到了一些东西。

  1. 枚举不是类型。

enum is not a type which means that it generates js code not like types and interfaces. https://github.com/ng-packagr/ng-packagr/issues/809#issuecomment-385187297

  1. 为了从 npm 模块导出枚举,您必须从主模块文件中显式导出它:

MyPackage/src/index.ts

import Card from './Card/Card'
import Input from './Input/Input'
import Form from './Form/Form'
import Link from './Link/Link'
import Text from './Text/Text'
import Button from './Button/Button'
import { HeroIcons } from './style/heroicons'

export { Card, Input, Form, Link, Text, Button, HeroIcons }
export * from './Input/Input.types'

现在我可以像这样导入它了:

import { Type } from '@myorg/component-library';