打字稿:如何在没有运行时工件的情况下制作通用版本的编译时集?

德塔瑞尔

我试图让 Typescript 警告我关于我们所拥有的 API 的不正确用法(我希望看到的错误被标记):

interface Icon { src: string; }

interface IconSet {
  [iconName: string]: Icon;
}

type IconRegistry<T> = {
  [K in keyof T]: Icon;
};

function asIconRegistry<T extends IconSet>(iconSet: T): IconRegistry<T> {
  return iconSet as any;
}

type UiBuilder = <P, S extends IconRegistry<P>>(iconRegistry: S) => {
  withIcon<K extends keyof S>(iconName: K): null,
};

const listIcons = asIconRegistry({
  sort: { src: 'sort.svg' }, // Expected to be OK
  email: { source: 'email.svg' }, // Expected to ERR
});

const profileIcons = asIconRegistry({
  security: { src: 'security.svg' }, // Expected to be OK
  email: { src: 'email.svg' }, // Expected to be OK
});

function buildUi(builder: UiBuilder) {
  builder(profileIcons).withIcon('security'); // Expected to be OK
  builder(profileIcons).withIcon('bold'); // Expected to ERR
}

它工作得很好,正如可以在这里看到的,除了一个小细节 - 该asIconRegistry函数是一个实际的运行时工件,这是不理想的。

我试过的:

手动输入变量:

const listIcons2: IconRegistry<{ sort: Icon, email: Icon }> = {
  sort: { src: 'sort.svg' }, // Expected to be OK
  email: { source: 'email.svg' }, // Expected to ERR
};

这有效,但太冗长了 - 泛型的用处有点丢失。除此之外,随着列表的大小和复杂性的增加,这变得更难维护,并且更难说服此 API 的消费者以这种方式键入他们的内容。

如何在没有运行时工件和上述冗长的情况下实现相同级别的类型安全?

杰卡兹

好吧,让我们清理一下。你的泛型比必要的要宽一些;大多数情况下,当您真正关心它们的键名时,您正在使用完整的对象类型。这是我的更改:

// Icon is the same
interface Icon { src: string; }

// you just need key names here (K), not a full object type (T)
type IconRegistry<K extends string> = Record<K, Icon>

// you don't really need IconSet, but for convenience, here it is
type IconSet = IconRegistry<string>

// the old P and S didn't do anything except determine keys
// let's just use those keys (K) directly
type UiBuilder = <K extends string>(iconRegistry: IconRegistry<K>) => {
  withIcon(iconName: K): null,
};

所以现在您已准备好创建和使用一些IconSets。

我真的不明白为什么您认为类型检查对运行时的影响为零很重要(身份函数调用的影响可能相当低,尤其是对于现代 JavaScript 引擎),但我喜欢挑战。您想在编译时验证listIconsprofileIcons是否有效IconSet,而不会将任何内容发送到 JavaScript 中。

这个怎么样:

type VerifyIconRegistry<T extends IconSet> = any

您需要将一个 validIconSet作为参数传递VerifyIconRegistry<>. 让我们看看它与 invalid 的作用listIcons

const listIcons = {
  sort: { src: 'sort.svg' },
  email: { source: 'email.svg' } 
};

declare var witness: 
  VerifyIconRegistry<typeof listIcons> // ERROR
  // Property 'src' is missing in type '{ source: string; }'

有错误。typeof listIcons不是有效的IconSet. Notedeclare var witness不会发出任何 JavaScript。它确实witness在编译时在此处添加了一个名为环境作用域的变量......给它任何你不需要的名字,你可以重用它,因为vars 可以重新声明。

现在为有效profileIcons

const profileIcons = {
  security: { src: 'security.svg' },
  email: { src: 'email.svg' }
};

declare var witness:
  VerifyIconRegistry<typeof profileIcons> // OKAY

那个有效(我们重用了这个witness名字)。

最后,让我们确保我们没有中断buildUi()

function buildUi(builder: UiBuilder) {
  builder(profileIcons).withIcon('security'); // OKAY
  builder(profileIcons).withIcon('bold'); // ERROR
  // Argument of type '"bold"' is not assignable 
  // to parameter of type '"security" | "email"'.
}

看起来不错。希望有所帮助;祝你好运!

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

在没有编译/运行时错误的情况下,如何识别损坏的VBA代码?

如何在没有运行时成本的情况下基于断言引导GCC优化?

如何在没有运行时开销的情况下轻松配置类?

Visual Studio 2015:在没有运行时库的情况下编译C / C ++

如何在没有CMD窗口的情况下运行jlink生成的Java运行时映像?

如何在没有调试器的情况下显示运行时发生异常的行号?

打字稿如何在没有引用的情况下复制对象

如何在没有绝对路径的情况下引用打字稿文件?

如何在没有操作的情况下使用 graphql schema.json 为 graphql 类型生成打字稿接口?

如何在没有.Net 的情况下制作项目?

如何在没有androidx的情况下制作菜单

如何在C ++ 11/14中使用类型安全性在没有宏的情况下实现运行时调试声明?

在没有运行窗口管理器的情况下运行时关闭屏幕

如何在没有cmd的情况下编译和运行Java文件?

如何在没有uuid的情况下编译fontconfig

当脚本在没有它的情况下正常运行时,为什么要使用`return`?

在没有运行时代码的情况下向对象类型添加新属性

如何在没有Snappy的情况下运行OrientDB?

如何在没有Artisan的情况下运行Laravel?

如何在没有dotenv的情况下运行项目?

我可以在没有 DLL 的情况下提供 .NET 服务,这些 DLL 需要编译但在运行时不需要吗?

如何在没有打字稿的情况下在Angular 2中注入Http

在启动时如何在没有root访问权限的情况下运行bash脚本?

如何在没有xdebug的情况下运行composer(与Homebrew一起安装时)

如何在不构造对象的情况下,在运行时设置数组大小?

在我的情况下如何在运行时填充数据?

如何在不嵌入文字的情况下创建 Flow Union 运行时优化

在没有 iBeacon 的情况下实施地理围栏时,当应用程序未运行时不会调用 didEnterRegion 和 didDetermineState

在没有看到控制台的情况下运行时如何检测unity c#是否有错误?