我正在使用一个包中的函数,该函数返回值为any

// node_modules/reflect-metadata/index.d.ts
function getMetadata(metadataKey: any, target: Object): any;

就像这样;

const example = getMetadata('string', myObj);

我还使用eslint作为TypeScrip,它在默认配置上强制执行安全分配.我得到上面一行的错误,因为getMetadata的类型签名返回并将类型any分配给example.

将值指定为any不安全

我真的不想禁用这条规则,为这行破例.如何手动缩小这种库函数类型的范围?

推荐答案

可能是通过使用包装器,而不是重载.包装器中的操作取决于您希望代码的类型安全程度.

您只需假设数据将以您期望的方式呈现,在这种情况下,您只需要一个包装器,但出错的机会很多:

// Lots of opportunities for error here
import { getMetadata as realGetMetadata } from "wherever";
// ...
export function getMetadata<T>(key: string): T {
    return realGetMetadata(key) as T;
}

(I've left off the 100 parameter there and below because I don't know what it's for, but it's largely tangential to the discussion.)

但同样,这并不是非常安全的类型,您只是在断言您拥有正确的值.理想情况下,在开发和试运行阶段达到least岁时,您希望证明断言是正确的,如果不正确则会引发错误.

import { getMetadata as realGetMetadata } from "wherever";
// ...
export function getMetadataString(key: string) {
    const value = realGetMetadata(key);
    if (typeof value !== "string") {
        throw new ValidationError(/*...*/);
    }
    return value;
}
export function getMetadataNumber(key: string) {
    const value = realGetMetadata(key);
    if (typeof value !== "number") {
        throw new ValidationError(/*...*/);
    }
    return value;
}
// ...

您最终得到了一系列函数(特别是在有许多不同的对象格式需要验证的情况下),但是您正在确保类型安全.

为了避免有太多的函数,您可以让验证器函数与键一起传递,但除非您有某种方式聚合它们的方法,否则如果您需要验证对象或元组 struct ,您最终得到的函数(加上基本调用)也是一样多.

为了解决这个问题,您可以使用旨在以类型安全的方式验证或解析数据的库.其中一个这样的库是zod,但虽然我将以它为例,但它并不是城里唯一的游戏,如果你愿意,你甚至可以自己玩.一百零二

下面是一个zod个例子:

import { z } from "zod";
import { getMetadata as realGetMetadata } from "wherever";
// ...
export function getMetadata<
    SchemaType extends z.ZodTypeAny,
    ResultType extends ReturnType<SchemaType["parse"]>
>(key: string, schema: SchemaType): ResultType {
    const rawValue = realGetMetadata(key);
    const value = schema.parse(rawValue);
    return value;
}

然后,例如:

// To get a string:
const str = getMetadata("foo", z.string());
// To get a number:
const num = getMetadata("foo", z.number());
// To get an object with `name` and `age` properties:
const num = getMetadata("foo", z.object({
    name: z.string(),
    age: z.number(),
}));
// ...

正如您从上面的代码中看到的,zod个对象有一个parse函数,其返回类型与您所描述的类型匹配.因此,在上面的代码中,返回类型是string,然后是number,然后是{name: string; age: number; }.这种方法的一个优点是,执行元数据检索的代码定义了它期望看到的内容.(可以像上面那样内联,也可以通过创建和重用架构对象来实现.)

If你想避免验证的运行时成本,你could在生产环境中绕过它(基于环境的分支,只需用类型断言将rawValue分配给value).在某些情况下,这可能是合适的,尽管这取决于您使用的库(如果有的话)和您使用的功能.例如,大多数(我假设)将超越简单的"它是字符串吗?或者是一个数字?"and offer"至少有N个字符长吗?或者是在X和Y之间?"你可能也想在生产中使用它.

Typescript相关问答推荐

TS 2339:属性切片不存在于类型DeliverableSignal产品[]上>'

Angular 17 -如何在for循环内创建一些加载?

如果在TypeScript中一个原始的类方法是递归的,如何使用类decorator 模式?

有没有可能使用redux工具包的中间件同时监听状态的变化和操作

typescribe警告,explate—deps,useEffect依赖项列表

如何在Typescript 中输入两个相互关联的变量?

来自类型脚本中可选对象的关联联合类型

PrimeNG日历需要找到覆盖默认Enter键行为的方法

如何重构长联合类型?

如何使我的函数专门针对联合标记?

使用Redux Saga操作通道对操作进行排序不起作用

正交摄影机正在将渲染的场景切成两半

Angular material 无法显示通过API获取的数据

如何从上传文件中删除预览文件图标?

为什么看起来相似的代码在打字时会产生不同的错误?

有没有一种方法可以防止我的组件包在其中安装样式组件'; node 模块中的s目录

在tabel管理区域react js上设置特定顺序

如何使用IsEqual创建断言类型相等的实用程序

使用受保护的路由和入门来响应路由

当受其他参数限制时,通用参数类型不会缩小