gpt4 book ai didi

typescript - 使用 Typescript 从过滤器中获取强类型

转载 作者:行者123 更新时间:2023-12-04 07:13:44 26 4
gpt4 key购买 nike

如果有人找到任何可以生成强类型映射/减少的好类型实用程序,我很感兴趣。例如,如果我有以下数组:

const arr = [
{ id: 1, value: "foobar" },
{ id: 2, value: () => "hello world" }
] as const;
假设我想要以下内容:
  • 减少到那些在 中有字符串的元素值(value) 房产
  • 在返回数组上保留强类型

  • 对于运行时,这是对内置 filter() 的极其简单的使用。内置在 Array 原型(prototype)中:
    const reduced = arr.filter(i => typeof i.value === "string");
    但是从这里返回的类型与以前没有变化。我想要的是让类型系统也响应缩减的数组。为了做到这一点,我有一个函数 isString它在运行时测试给定的输入是否是字符串,但也返回 bool 文字,以便类型系统可以推断出返回的真/假值。
    作为引用, isString实用程序如下所示:
    type IsString<T> = T extends string ? true : false;

    function isString<T>(i: T) {
    return (typeof i === "string") as IsString<T>;
    }
    此实用程序适用于类型和运行时系统,因此天真地希望以下内容可以工作:
    const reduced2 = arr.filter(i => isString(i));
    不幸的是,这仍然只是返回相同的联合类型。如何让这个工作?
    注意:我也想做其他 map-reduce 功能,但过滤操作对我来说是最紧迫的。

    Playground

    最佳答案

    a call signature for the ReadonlyArray filter() method in the TypeScript standard library形式的

    interface Array<T> {
    filter<S extends T>(
    predicate: (value: T, index: number, array: readonly T[]) => value is S,
    thisArg?: any
    ): S[];
    }
    这意味着如果您传入 predicate编译器理解为 user-defined type guard function; i.e., one that returns a type predicate 的回调,则返回类型为 filter()可以是比原始数组更窄的数组类型。
    这是我能想到的唯一可行的方法。

    一个问题是编译器无法推断出 i => typeof i.value === "string"。就是这样一个类型保护函数。或者,更一般地说,编译器不会从任何函数实现的主体中推断类型谓词返回类型。如果您希望函数成为类型保护函数,则需要手动对其进行注释。
    microsoft/TypeScript#38390 有功能请求要求对类型保护函数的自动推断提供一些支持,也许如果以及何时实现,那么 i => typeof i.value === "string"会神奇地做你想做的事。但就目前而言,无论如何我们都需要放弃这一点。

    一种可能的继续方式如下所示:
    const hasStringValue = <T extends { value: any }>(
    i: T): i is Extract<T, { value: string }> => typeof i.value === "string"
    函数 hasStringValuegeneric输入 i 的类型保护函数类型 T constrained{value: any} , 并返回 boolean可用于缩小 i 的值输入 Extract<T, {value: string}>如果 true .我正在使用 the Extract utility type因为我们期望 T成为 union类型,其中一些可分配给 {value: string} .
    让我们试一试:
    /* const arr: readonly [{
    readonly id: 1;
    readonly value: "foobar";
    }, {
    readonly id: 2;
    readonly value: () => string;
    }] */

    const reduced = arr.filter(hasStringValue);

    /* const reduced: {
    readonly id: 1;
    readonly value: "foobar";
    }[] */

    console.log(
    reduced.map(i => i.value.toUpperCase())
    ) // ["FOOBAR"]
    看起来挺好的。原始数组 arr有一个元素类型的联合,以及 filter() 的输出是一个新数组 reduced其元素类型仅与 id: 1 对应.

    因为我们是“手工”完成的,所以有一些警告。首先,编译器不知道如何检查您是否正确实现了用户定义的类型保护功能;见 microsoft/TypeScript#29980 .所以没有什么能阻止你这样做:
    const hasStringValue = <T extends { value: any }>(
    i: T): i is Extract<T, { value: string }> => typeof i.value !== "string"
    // oops ----------------------------------------------------> ~~~
    在没有编译器警告您的情况下,这会在运行时导致坏事:
    const reduced = arr.filter(hasStringValue);
    console.log(reduced.map(i => i.value.toUpperCase())) // no compiler warning, but
    // 💥 RUNTIME ERROR! i.value.toUpperCase is not a function
    这只能通过小心来解决。
    其次,您的数组元素也可能属于 T 类型。在哪里 Extract<T, {value: any}>保留或消除您不想要的类型。例如,也许 value比字符串宽:
    const hmm = [{ value: Math.random() < 0.5 ? "hello" : 123 }];
    hmm.filter(hasStringValue) // never[]
    哎呀, Extract<{value: string | number}, {value: string}>never ,因为左侧不能分配给右侧。
    这可以通过更改 hasStringValue 来解决。做更复杂的事情,例如检查潜在的重叠而不是子类型。我不打算在这里切线这样做,尤其是因为其他 T类型可能还有其他需要担心的极端情况。关键是您需要弄清楚哪种类型的谓词实际上对您的输入类型起作用,因此您必须进行测试。
    所以,呃,小心点。
    Playground link to code

    关于typescript - 使用 Typescript 从过滤器中获取强类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68900068/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com