TypeScript import type 和 "没有导出的成员" 的坑

Vue 项目里想约束一个变量只能是 element-plus tag 的合法类型:

import type { TagType } from "element-plus";

const t: TagType = "success";

结果 TS 报错:

模块 "element-plus" 没有导出的成员 "TagType"。
你是想改用 "import TagType from 'element-plus'" 吗?

问题不是 import type 用错了,是 element-plus 根本没在主入口导出 TagType

import type 是干嘛的

先说 import type 本身,它只导入类型信息,编译成 JS 时会完全消失:

import type { User } from "./types";

const u: User = { id: 1 };   // 编译后 import 语句被抹掉

对比普通 import:

import { User } from "./types";
// 编译后仍然 require("./types") — 运行时如果这个模块只导出类型就报错

import type 的三个作用:

  1. 避免运行时副作用 — 只用类型的场景不会拉进模块
  2. 配合 verbatimModuleSyntax — 现代 TS 项目里”类型 vs 值”必须显式区分
  3. 减小打包体积 — 无用运行时代码会被 tree-shake 掉

为什么 element-plus 报”没有导出的成员”

TagType 在 element-plus 内部定义:

// packages/components/tag/src/tag.ts
export type TagType = "" | "success" | "info" | "warning" | "danger";

没转发到主入口 element-plus/es/index.d.ts。所以:

import type { TagType } from "element-plus";     // 找不到

两种解法

方案 1:自己定义(推荐)

三秒钟搞定,稳定:

type TagType = "" | "success" | "info" | "warning" | "danger";

const t: TagType = "success";

对内部类型 element-plus 后续升级也不会一言不合就改。自己定义等于 API 边界完全掌控。

方案 2:从深路径引

理论上可以:

import type { TagType } from "element-plus/es/components/tag/src/tag";

局限:

  • 这是非公开 API,element-plus 版本升级可能改路径
  • 需要构建工具能解析这种深路径
  • 不同版本的 element-plus 目录结构可能不同(lib/ vs es/ vs dist/

做工具库或第三方组件时优先方案 1,业务代码里图省事偶尔可以用方案 2。

import type 的常见变体

内联 type(TS 4.5+):

import { type User, createUser } from "./user";
//        ^^^^ 只这个是类型

同一 import 里混装类型和值,编译后 User 被抹掉,createUser 保留。

全部当值 import(老式,不推荐):

import { User } from "./user";
type MyRole = User["role"];    // 运行时会真的加载模块

export type

export type { User } from "./user";

再转发类型,同样只影响类型系统,不产生运行时依赖。

一张速查表

场景写法
只用类型import type { X } from "..."
同一 import 混装import { type X, y } from "..."
转发类型export type { X } from "..."
库没有导出的内部类型自己定义 or 深路径引(慎用)
verbatimModuleSyntax=true类型必须用 import type,否则报错

一句话总结

import type = 只要类型不要运行时代码。库没导出的内部类型别硬拽——自己定义一份最稳,深路径 import 只是应急。