[英]How do you define a generic getter method in Typescript with multiple overloads?
I'm trying to define a method that operates as a getter, taking an optional parameter.我正在尝试定义一个作为 getter 操作的方法,采用可选参数。 The getter provides access to an object of type T
, and should return either the entire object, or a property on that object. getter 提供对T
类型的 object 的访问,并且应该返回整个 object 或该 object 上的属性。
The challenge is that I am trying to defined the method in two places, first in an interface, and second in the actual implementation.挑战在于我试图在两个地方定义方法,第一个在接口中,第二个在实际实现中。
Here's my approach:这是我的方法:
// Getter defines both overloads
interface StoreGetter {
<T>(): T;
<T, K extends keyof T>(prop: K): T[K];
}
// Store has a generic type, and exposes that type and properties on that type
interface Store<T> {
get: StoreGetter;
// Either one works individually
// get: <T>() => T;
// get: <T, K extends keyof T>(prop: K) => T[K];
}
export function makeStore<T>(initial: T): Store<T> {
let value: T = initial;
// Apparently, you can only define overloads via a function declaration
// function get<T>(): T;
// function get<T, K extends keyof T>(prop: K): T[K];
function get(prop?: keyof T) {
if (typeof prop !== 'undefined') {
return value[prop];
}
return value;
}
return {
get,
};
}
const store = makeStore({
text: '',
items: [],
num: 1
});
// Argument of type '"text"' is not assignable to parameter of type 'never'.(2345):
store.get('text')
// Object is of type 'unknown'.(2571)
store.get().
Unfortunately, the two definitions seem to clobber each other.不幸的是,这两个定义似乎相互冲突。
How can I define this method with overloads, and have correct type inference for both calls?如何使用重载定义此方法,并为两个调用提供正确的类型推断?
After many failed attempts, I've discovered one configuration that produces the expected inferences:经过多次失败的尝试,我发现了一种产生预期推论的配置:
interface StoreGetter<T> {
(): T;
<K extends keyof T>(props: K): T[K];
}
interface Store<T> {
get: StoreGetter<T>;
set: (val: any | T) => void;
}
export function makeStore<T>(initial: T): Store<T> {
let value: T = initial;
let listeners: Function[] = [];
function get(): T;
function get<K extends keyof T>(prop: K): T[K];
function get(prop?: keyof T): T | T[keyof T] {
if (typeof prop !== 'undefined') {
return value[prop];
}
return value;
}
return {
get,
set: (val: any) => {
value = {
...value,
...val,
};
listeners.forEach(fn => fn(value));
}
};
}
const store = makeStore({
text: '',
items: [],
num: 1
});
// Both work with type inference
store.get('text').toUpperCase
store.get().items
Still hoping to find a way to do it with an inline/anonymous function.仍然希望找到一种使用内联/匿名 function 的方法。
On a positive note, this approach works seamlessly in a declarations file (eg, store.d.ts), enabling the use of a single declaration:积极的一点是,这种方法可以在声明文件(例如 store.d.ts)中无缝工作,从而可以使用单个声明:
interface StoreGetter<T> {
(): T;
<K extends keyof T>(props: K): T[K];
}
interface Store<T> {
get: StoreGetter<T>;
}
export function makeStore<T>(initial: T): Store<T>;
export function useStore<T>(store: T, prop?: string): [T|any, (newState: T|any) => void];
And then in a separate JS file:然后在一个单独的 JS 文件中:
const store = makeStore({
keypresses: 0,
text: '',
arrows: [],
});
// Both inferred:
store.get('keypresses').toFixed
store.get().arrows.push
This produces the expected annotations in VS code:这会在 VS 代码中生成预期的注释:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.