typescript - 如何将 TypeScript 泛型与绑定一起使用?
问题描述
我正在尝试将不受限制的开源库移植到TypeScript。
它提供了一个binder
函数,该函数接受一个方法,将它与任何参数一起绑定到上下文,并返回结果并.unbounded
设置为原始方法。
在 JavaScript 中是:
function binder (method, context, …args) {
const unbounded = method.unbounded || method
const bounded = method.bind(context, ...args)
Object.defineProperty(bounded, 'unbounded', {
value: unbounded,
enumerable: false,
configurable: false,
writable: false
})
return bounded
}
但是,尽管进行了多次尝试,但我终其一生都无法弄清楚如何用 TypeScript 来表达这一点。
我所有的尝试都没有达到以下期望:
// arguments should work correctly
function a(this: { local: string }, arg1: string): string {
return this.local + arg1
}
// should fail:
binder(a, null) // context wrong type
binder(a, {local: 123}) // local wrong type
binder(a, {local: 123}, 'bar') // local wrong type
binder(a, {local: 'foo'})() // arg1 is missing
binder(a, {local: 'foo'}, 123) // arg1 wrong type
binder(a, {local: 'foo'})(123) // arg1 wrong type
binder(a, {local: 123}, 123) // local and arg1 wrong type
binder(a, {local: 123})(123) // local and arg1 wrong type
binder(a, {local: 'foo'})('bar', 123) // extra arg
binder(a, {local: 'foo'})('bar', 'baz') // extra arg
// should pass:
binder(a, {local: 'foo'})
binder(a, {local: 'foo'}, 'bar')()
binder(a, {local: 'foo'})('bar')
// return value should work correctly
// should pass
binder(a, {local: 'foo'}, 'bar')().substr(0)
binder(a, {local: 'foo'})('bar').substr(0)
// extensions should work correctly
interface E extends Function {
(this: { local: string }, arg1: string): string
extension?: string
}
const b = function(this: { local: string }, arg1: string): string {
return this.local + arg1
} as E
b.extension = 'hello'
const bb = binder(b, { local: 'foo' }, 'bar')
// bb.extension should not be present
// bb.unbounded.extension should be present
// bb.unbounded.unbounded should not be present
const bbb = binder(bb, { local: 'foo' }, 'bar')
// bbb.extension should not be present
// bbb.unbounded.extension should be present
// bbb.unbounded.unbounded should not be present
我认为 TypeScript 3.2 CallbableFunction在这里会有所帮助,但我不知道如何使用它。TypeScript 的实现似乎也有一个错误,它会阻止返回类型的准确性。
我究竟做错了什么?
解决方案
我们可以采用3.2bind
中引入的重载并修改它们以转发您添加到函数的属性。unbounded
type UnboundFunction<T extends Function, TUnbound> = T & {
unbounded?: TUnbound
}
type BoundFunction<T extends Function, TUnbound> = T & {
unbounded: TUnbound
}
type BoundFunctionHelper<TResult extends Function, TUnbound, TArgument> = BoundFunction<TResult, undefined extends TUnbound ? TArgument : TUnbound >;
export function binder<T, R, TUnbound = undefined>(method: UnboundFunction<(this: T) => R, TUnbound>, thisArg: T): BoundFunctionHelper<() => R, TUnbound, typeof method >;
export function binder<T, A extends any[], R, TUnbound = undefined>(method: UnboundFunction<(this: T, ...args: A) => R, TUnbound>, thisArg: T): BoundFunctionHelper<(...args: A) => R, TUnbound, typeof method >;
export function binder<T, A0, A extends any[], R, TUnbound = undefined>(method: UnboundFunction<(this: T, arg0: A0, ...args: A) => R, TUnbound>, thisArg: T, arg0: A0): BoundFunctionHelper<(...args: A) => R, TUnbound, typeof method >;
export function binder<T, A0, A1, A extends any[], R, TUnbound = undefined>(method: UnboundFunction<(this: T, arg0: A0, arg1: A1, ...args: A) => R, TUnbound>, thisArg: T, arg0: A0, arg1: A1): BoundFunctionHelper<(...args: A) => R, TUnbound, typeof method>
export function binder<T, A0, A1, A2, A extends any[], R, TUnbound = undefined>(method: UnboundFunction<(this: T, arg0: A0, arg1: A1, arg2: A2, ...args: A) => R, TUnbound>, thisArg: T, arg0: A0, arg1: A1, arg2: A2): BoundFunctionHelper<(...args: A) => R, TUnbound, typeof method>;
export function binder<T, A0, A1, A2, A3, A extends any[], R, TUnbound = undefined>(method: UnboundFunction<(this: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3, ...args: A) => R, TUnbound>, thisArg: T, arg0: A0, arg1: A1, arg2: A2, arg3: A3): BoundFunctionHelper<(...args: A) => R, TUnbound, typeof method>;
export function binder<T, AX, R, TUnbound = undefined>(method: UnboundFunction<(this: T, ...args: AX[]) => R, TUnbound>, thisArg: T, ...args: AX[]): BoundFunctionHelper<(...args: AX[]) => R, TUnbound, typeof method>;
export function binder(method: UnboundFunction<Function, Function>, context: any, ...args: any[]): BoundFunction<Function, Function> {
const unbounded = method.unbounded || method
const bounded = method.bind(context, ...args)
Object.defineProperty(bounded, 'unbounded', {
value: unbounded,
enumerable: false,
configurable: false,
writable: false
})
return bounded
}
function a(this: { local: string }, arg: string): string {
return this.local
}
binder(a, null) // fails as this.local is missing
binder(a, {local: 'foo'})() // fails as arg1 is missing
binder(a, {local: 'foo'}, 'bar')() // pass
binder(a, {local: 'foo'})('bar') // pass
binder(a, {local: 'foo'}, 123)() // fail due to incorrect type
binder(a, {local: 123}, 'bar')() // fail due to incorrect type
binder(a, {local: 'foo'})('bar') // fail due to incorrect type
binder(a, {local: 'foo'})('bar', 'bob') // fail due to extra arg
binder(a, {local: 'foo'}, 'bar', 'bob') // unfortunately works, also a problem with the 3.2 implementation of bind.
// Preserve unbounded type test
function a2(this: { local: string }, arg: string, arg2: string): string {
return this.local
}
var unbounded = binder(binder(a2, { local: 'foo'}), undefined, "").unbounded;
var o = { local : "", unbounded };
o.unbounded("", "") // unbounded type reserved trhough mutiple binder calls
推荐阅读
- python - sherlock要求安装问题
- r - 基于大空间多边形数据框列的 Shiny Leaflet Input 滑块中的 Chloropleth 地图着色
- java - 在数组中使用 java Math 类时出错
- java - Spring Data JPA项目,如何限制嵌套对象的数量?@OneToMany
- java - Java If /Else 语句命令字符串
- firebase - 我可以使用 Google Cloud Armor 以编程方式将许多 IP 列入白名单吗?
- javascript - reactjs createRef 在组件数组中不起作用
- pandas - Pandas 中的快速参考长名称
- java - Jackson 不会使用自定义序列化器序列化 null
- flutter - 在 Streambuilder 中的集合上使用 where() 不断返回 null