【问题标题】:How to type a function that have properties and takes generic parameters如何键入具有属性并采用泛型参数的函数
【发布时间】:2020-01-20 12:20:08
【问题描述】:

假设我有一个具有属性userId 的函数,userId 与其参数和返回类型之间应该有一些约束。喜欢:

interface IMyfunc<T> {
    (item: T): T
    userId: T
}

它的实现是这样的:

const func: IMyfunc<number> = a => a
func.userId = 1

到目前为止一切都很好,但是,我怀疑我现在只能用数字调用func,而不能用字符串或其他东西调用它,因为我必须显式绑定它的类型使用IMyfunc接口时的参数。

所以如果我需要func 来获取字符串参数,我必须声明另一个函数express 使用IMyfuncstring 类型绑定到它的类型参数,是什么让我觉得这里的泛型类型失去了意义真正的通用

有什么方法可以让我输入一个具有属性并采用泛型参数的函数真的是泛型的吗?

【问题讨论】:

  • 您可以使用 const func:IMyfunc = a => a 实现您所谓的“真正的泛型”,但您会失去类型安全性。 Typescript 在赋值时需要知道类型
  • @DPro 是的,所以我想知道有没有什么方法可以让这种函数真正通用而不会失去类型安全性。
  • 嗯,你能看一个用例吗?假设我有一个名为func 的“真正的通用”值。我知道如果你打电话给func(3),你会得到一个number,如果你打电话给func("a"),你会得到一个string,但是func.userId的类型是什么?
  • @jcalz emm,如果实现是 const func: IMyfunc&lt;number&gt; = a =&gt; { func.userId = a; return a } 可能对您的用例有意义。每次调用func 时,func.userId 应该是func 的参数类型。
  • 您要求的是“type state”或“type mutation”,其中值的类型可以根据值被使用。 TypeScript 可以通过控制流分析来缩小某些值的类型,但目前还没有表示任意类型突变的方法。即将到来的asserts feature 更接近了,但仍然不会以您想要的方式“堆叠”(大概您希望func(0); const a = func.userId; func("a"); const b = func.userId 推断numberastringb,但可能你会为b 获取never

标签: typescript generics typescript-generics


【解决方案1】:

这个问题是关于“真正的”泛型,我将其解释为任意generic values,TypeScript 目前不支持这些泛型。 TypeScript 唯一的泛型值是泛型函数。


但我认为您真正要求的是“类型状态”或“类型突变”,其中值的类型可以根据值的使用方式而改变。我认为,意图看起来像这样:

func(4);
const n = func.userId; // n inferred as number
func("a");
const s = func.userId; // s inferred as string
func(false);
const b = func.userId; // b inferred as boolean

...func.userId 的类型会根据之前对 func() 的调用而改变。

不幸的是,虽然 TypeScript 可以通过 control flow analysis缩小某些值的类型(因此 string | number 类型的值可以缩小为 string 类型的值),但目前没有表示任意类型突变的方式(例如,将string 类型的值更改为number 类型的值)。

即将到来的asserts feature(预定为TS3.7)至少应该允许在类型系统中表示这种缩小,因此您可以使func(4)缩小func从类型IMyfunc&lt;unknown&gt;到@ 987654339@。但是您仍然可能无法进行您正在寻找的任意突变。从IMyfunc&lt;number&gt; 更改为IMyfunc&lt;string&gt; 并没有缩小范围;您首先必须以某种方式扩大回IMyfunc&lt;unknown&gt;,而且我认为没有任何方法可以做到这一点。

comment on the asserts feature pull request 中讨论了类似于您的用例的内容,而无法扩展的问题是pointed out。也许最终这将是可行的,或者类似的东西?不确定。


在任何情况下,静态类型系统的主要驱动因素之一是表达式具有一个静态类型,该类型表示它可以采用的一组可能值,并且静态类型不会改变。这适用于支持无状态和不变性的函数式编程技术,并且通常在命令式编程可能改变现有值的情况下返回一个新值。

如果我们改变代码来做那个,你会得到类似下面的东西:

type Func<T> = {
  userId: T;
  <U>(item: U): Func<U>;
};

这里的Func&lt;T&gt; 是一个userId 类型为T 的类型,但是当您将它作为带有U 类型参数的函数调用时,它返回一个Func&lt;U&gt;。然后,您可以丢弃 Func&lt;T&gt; 并使用新的 Func&lt;U&gt;(它的 userId 类型为 U)。

一种可能的实现如下所示:

function func<U>(item: U): Func<U> {
  const f = <T>(item: T) => func(item);
  return Object.assign(f, { userId: item });
}

这个实现是无状态的;它永远不会修改自己或传入的项目。你可以这样使用它:

const f = func(4);
const n = f.userId; // number
console.log(n); // 4

然后你使用f 而不是func

const g = f("a");
const s = g.userId; // string
console.log(s); // 1

当然,在上述实现中,您可以重用func(),因为它是无状态的:

const h = func(false);
const b = h.userId; // boolean
console.log(b); // false

在这种情况下,您实际上并不需要所有这些复杂性......您只需实现如下代码:

function func<U>(item: U): { userId: U } {
  return { userId: item };
}

const f = func(4);
const n = f.userId; // 4
console.log(n); // 4

const g = func("a");
const s = g.userId; // string
console.log(s); // 1

const h = func(false);
const b = h.userId; // boolean
console.log(b); // false

在这一点上,我已经解开了这种情况,您刚刚获得了一个返回包装对象的函数。我可能会推荐这个而不是任何带有突变的东西,因为它在类型系统中的表现要好得多。但你比我更了解你的用例。


无论如何,希望对您有所帮助;祝你好运!

Link to code

【讨论】:

    【解决方案2】:

    您无法通过调用对象的方法来更改对象的类型(在您的情况下为字段分配值)。您可以使用以下内容:

    function setUserId<T>(userId: T, func: IMyfunc<unknown>): IMyfunc<T> {
      func.userId = userId;
      return <IMyfunc<T>>func;
    }
    

    【讨论】:

    • 使用func(比如const func: IMyfunc&lt;number&gt; = a =&gt; a)调用setUserId 时出错:“'IMyfunc' 类型的参数不可分配给'IMyfunc'。参数'item'和'item'的类型不兼容。类型'unknown'不能分配给类型'number'。"
    • 但对我来说这是一个新问题,为什么错误消息是“unknow is not assignable to type number”而不是“number is not assignable to type unknow”?
    • 哦我明白了,因为item是函数参数,func: IMyfunc&lt;unknown&gt;可以接受unknow类型的参数,而const func: IMyfunc&lt;number&gt; = a =&gt; a)只能接受number类型的参数。跨度>
    猜你喜欢
    • 2022-01-04
    • 2010-10-04
    • 2023-01-28
    • 1970-01-01
    • 2021-06-15
    • 2020-11-27
    • 1970-01-01
    • 2018-02-18
    相关资源
    最近更新 更多