如何键入具有属性并采用通用参数的函数
How to type a function that have properties and takes generic parameters
假设我有一个具有 属性 userId
的函数,并且 userId
和它的参数以及 return 类型之间应该有一些约束。喜欢:
interface IMyfunc<T> {
(item: T): T
userId: T
}
它的实现是这样的:
const func: IMyfunc<number> = a => a
func.userId = 1
到目前为止一切顺利,但是,我怀疑我现在只能用数字调用 func
而不能用字符串或其他东西调用它,因为我 必须 当我使用 IMyfunc
接口时显式绑定它的类型参数。
因此,如果我需要 func
来获取字符串参数,我必须声明另一个使用 IMyfunc
且 string
类型绑定到其类型参数的函数表达式,这让我想到这里的泛型失去了真正的泛型.
的意义
有什么方法可以键入具有属性并采用通用参数的函数吗?
您无法通过调用对象的方法来更改对象的类型(在您的情况下为字段赋值)。不过,您可以使用以下内容:
function setUserId<T>(userId: T, func: IMyfunc<unknown>): IMyfunc<T> {
func.userId = userId;
return <IMyfunc<T>>func;
}
问题涉及 "real" 泛型,我将其解释为任意 generic values,目前 TypeScript 不支持它们。 TypeScript 唯一的泛型值是泛型函数。
但我认为您真正要求的是 "type state" 或 "type mutation",其中值的类型可以根据值的使用方式而改变。我认为,意图看起来像这样:
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
的值被更改为类型 [=22= 的值) ]).
即将推出的 asserts
feature (slated for TS3.7) 至少应该允许在类型系统中表示这种缩小,因此您可以从类型 [=26] 中缩小 func(4)
func
=] 到 IMyfunc<number>
。但是您仍然可能无法进行您正在寻找的任意突变。从 IMyfunc<number>
更改为 IMyfunc<string>
并没有缩小;您首先必须以某种方式扩大到 IMyfunc<unknown>
,我认为没有任何方法可以做到这一点。
comment on the asserts
feature pull request, and the can't-widen-back problem was pointed out 中讨论了类似您的用例的内容。也许最终这将是可行的,或者类似的东西?不确定。
无论如何,静态类型系统的主要驱动因素之一是表达式具有静态类型,表示它可以采用的一组可能值,并且静态类型不会改变。这适用于支持无状态和不变性的函数式编程技术,并且通常 return 在命令式编程可能改变现有值的情况下的新值。
如果我们更改代码以执行 ,您将得到更类似于以下内容的内容:
type Func<T> = {
userId: T;
<U>(item: U): Func<U>;
};
这里的 Func<T>
是一个带有 userId
类型 T
的类型,但是当你将它作为一个带有 U
类型参数的函数调用时,它return 是 Func<U>
。然后您可以丢弃 Func<T>
并使用新的 Func<U>
(它有一个类型为 U
的 userId
)。
一种可能的实现如下所示:
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
在这一点上,我已经阐明了您刚刚获得 return 是一个包装对象的函数的程度。我可能会推荐这个而不是任何带有突变的东西,因为它更适合类型系统。但是你比我更了解你的用例。
总之,希望对你有所帮助;祝你好运!
假设我有一个具有 属性 userId
的函数,并且 userId
和它的参数以及 return 类型之间应该有一些约束。喜欢:
interface IMyfunc<T> {
(item: T): T
userId: T
}
它的实现是这样的:
const func: IMyfunc<number> = a => a
func.userId = 1
到目前为止一切顺利,但是,我怀疑我现在只能用数字调用 func
而不能用字符串或其他东西调用它,因为我 必须 当我使用 IMyfunc
接口时显式绑定它的类型参数。
因此,如果我需要 func
来获取字符串参数,我必须声明另一个使用 IMyfunc
且 string
类型绑定到其类型参数的函数表达式,这让我想到这里的泛型失去了真正的泛型.
有什么方法可以键入具有属性并采用通用参数的函数吗?
您无法通过调用对象的方法来更改对象的类型(在您的情况下为字段赋值)。不过,您可以使用以下内容:
function setUserId<T>(userId: T, func: IMyfunc<unknown>): IMyfunc<T> {
func.userId = userId;
return <IMyfunc<T>>func;
}
问题涉及 "real" 泛型,我将其解释为任意 generic values,目前 TypeScript 不支持它们。 TypeScript 唯一的泛型值是泛型函数。
但我认为您真正要求的是 "type state" 或 "type mutation",其中值的类型可以根据值的使用方式而改变。我认为,意图看起来像这样:
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
的值被更改为类型 [=22= 的值) ]).
即将推出的 asserts
feature (slated for TS3.7) 至少应该允许在类型系统中表示这种缩小,因此您可以从类型 [=26] 中缩小 func(4)
func
=] 到 IMyfunc<number>
。但是您仍然可能无法进行您正在寻找的任意突变。从 IMyfunc<number>
更改为 IMyfunc<string>
并没有缩小;您首先必须以某种方式扩大到 IMyfunc<unknown>
,我认为没有任何方法可以做到这一点。
comment on the asserts
feature pull request, and the can't-widen-back problem was pointed out 中讨论了类似您的用例的内容。也许最终这将是可行的,或者类似的东西?不确定。
无论如何,静态类型系统的主要驱动因素之一是表达式具有静态类型,表示它可以采用的一组可能值,并且静态类型不会改变。这适用于支持无状态和不变性的函数式编程技术,并且通常 return 在命令式编程可能改变现有值的情况下的新值。
如果我们更改代码以执行 ,您将得到更类似于以下内容的内容:
type Func<T> = {
userId: T;
<U>(item: U): Func<U>;
};
这里的 Func<T>
是一个带有 userId
类型 T
的类型,但是当你将它作为一个带有 U
类型参数的函数调用时,它return 是 Func<U>
。然后您可以丢弃 Func<T>
并使用新的 Func<U>
(它有一个类型为 U
的 userId
)。
一种可能的实现如下所示:
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
在这一点上,我已经阐明了您刚刚获得 return 是一个包装对象的函数的程度。我可能会推荐这个而不是任何带有突变的东西,因为它更适合类型系统。但是你比我更了解你的用例。
总之,希望对你有所帮助;祝你好运!