说我有一个具有属性userId
的函数,并且userId
与它的参数和返回类型之间应该有一些约束。喜欢:
interface IMyfunc<T> {
(item: T): T
userId: T
}
它的实现就像:
const func: IMyfunc<number> = a => a
func.userId = 1
但是到目前为止,我的疑问是我现在只能用数字调用func
,而不能用字符串或其他名称调用,因为我必须明确绑定使用IMyfunc
界面时输入其类型参数。
因此,如果我需要func
来获取字符串参数,则必须声明另一个函数,该函数将IMyfunc
类型绑定到其type参数上使用string
,这使我认为此处的泛型类型失去了真实泛型的含义。
我可以通过某种方式键入具有属性并采用通用参数的函数吗?
答案 0 :(得分: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
的值可以缩小为类型{{1}的值) }),目前没有任何表示任意类型突变的方法(例如,将类型string
的值更改为类型string
的值)。
即将到来的asserts
feature(计划为TS3.7)应该至少允许在类型系统中表示这种缩小,因此您可以使number
缩小{{1} }从类型func(4)
到func
。但是您仍然可能无法进行所需的任意突变。从IMyfunc<unknown>
更改为IMyfunc<number>
并没有缩小;您首先必须以某种方式扩大到IMyfunc<number>
,但我认为没有任何办法可以做到这一点。
在comment on the asserts
feature pull request中讨论了类似您的用例的问题,而无法扩展的问题是pointed out。也许最终这将是可行的,或者类似的事情?不确定。
在任何情况下,静态类型系统的主要驱动程序之一是表达式具有静态类型,该静态类型表示它可以采用的一组可能的值,并且静态类型不会更改。这与支持无状态和不变性的函数式编程技术很好地配合,并且在命令式编程可能会改变现有值的情况下,通常会返回新值。
如果我们更改代码以执行 操作,则会得到类似以下的内容:
IMyfunc<string>
此处IMyfunc<unknown>
是类型为type Func<T> = {
userId: T;
<U>(item: U): Func<U>;
};
的{{1}}类型,但是当您将其作为参数类型为Func<T>
的函数调用时,它将返回一个userId
。然后,您可以丢弃T
并使用新的U
(类型为Func<U>
的{{1}})。
一个可能的实现如下所示:
Func<T>
此实现是无状态的;它永远不会修改自己或传入的项目。您可以这样使用它:
Func<U>
然后您使用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
在这一点上,我已经阐明了这种情况,即您只有一个返回包装对象的函数。我可能会建议您使用此方法,而不要使用任何带有突变的方法,因为它在类型系统中的表现更为出色。但是您比我更了解您的用例。
无论如何,希望能有所帮助;祝好运!
答案 1 :(得分:0)
无法通过调用对象上的方法来更改对象的类型(在这种情况下,将值分配给字段)。您可以使用以下内容:
function setUserId<T>(userId: T, func: IMyfunc<unknown>): IMyfunc<T> {
func.userId = userId;
return <IMyfunc<T>>func;
}