typescript 重写返回任何

kgsdhlau  于 2023-11-20  发布在  TypeScript
关注(0)|答案(1)|浏览(151)

我使用了一个包中的函数,它返回一个值any;

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

字符串
像这样;

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


我也使用eslint作为typescript,它在默认配置上强制执行安全赋值。我在上面一行得到一个错误,因为getMetadata的类型签名返回并将类型any赋值给example
any值的不安全分配
我真的不想禁用这条规则,为这一行破例。我如何才能手动缩小这样的库函数类型?

wtzytmuj

wtzytmuj1#

可能是通过使用 Package 器,而不是重载。你在 Package 器中做什么取决于你希望代码有多类型安全。
你可以假设数据是你期望的那样,在这种情况下,你只需要一个 Package 器,但是错误的机会很多:

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

字符串

  • (我在这里和下面省略了target参数,因为我不知道它是用来做什么的,但它在很大程度上与讨论无关。

但同样,这不是非常类型安全的,你只是Assert你有正确的值。理想情况下,* 至少 * 在开发和阶段,你想证明Assert是正确的,如果不是,就会引发错误。一种方法是有多个函数:

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;
}
// ...


最后你会得到一堆函数(特别是当有很多不同的对象格式需要验证时),但你要确保类型安全。
为了避免有太多的函数,你可以让验证器函数和键一起沿着传递,但是除非你有某种方法来聚合它们,否则如果你需要验证对象或元组结构,你最终会得到同样多的函数(加上基本调用)。
为了科普这个问题,你可以使用一个库来以类型安全的方式验证或解析数据。这样的一个库是zod,但是虽然我将它作为一个例子,但它不是城里唯一的游戏,如果你愿意,你甚至可以自己玩。(我与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; }。这种方法的一个优点是,执行元数据检索的代码定义了它期望看到的内容。(可以像上面那样内联,或者通过创建和重用模式对象。

如果您想避免验证的运行时成本,您可以 * 在生产环境中绕过它(分支基于环境,只需使用类型Assert将rawValue分配给value)。在某些情况下,这可能是合适的,但这取决于您使用的库(如果有的话)和您使用的功能。例如,大多数(我假设)将超越简单的“它是一个字符串吗?”或“它是一个数字吗?”,并提供“它至少是N个字符长吗?”或“它是在X和Y之间吗?”,这可能也是您在生产中想要的。

相关问题