我正在写一些镜头功能:
interface Lens<C, V> = {
get: (container: C) => V;
set: (container: C, value: V) => C;
}
interface Container {
value: number;
other: string;
};
const valueLens: Lens<Container, number> = {
get: (container: Container) => container.value,
set: (container: Container, value: number): Container => ({ ...container, value }),
};
简单的物镜的图案非常明显,所以我想对其进行抽象:
function makeFieldLens<T, K extends keyof T>(key: K): Lens<T, T[K]> {
return {
get: (container: T): T[K] => container[key],
set: (container: T, value: T[K]): T => ({ ...container, [key]: value }),
};
}
但是由于不能部分指定模板,因此需要在通用类型参数中同时指定T
和K
,这变得非常麻烦且重复:
const valueLens = makeFieldLens<Container, 'value'>('value');
const value: number = valueLens.get(container);
我试图找到一种方法来推断K extends keyof T
:
为编译器提供默认类型key
只是将typeof key
设置为'value' | 'other'
,这意味着get
的返回类型不仅仅是number | string
{1}}:
number
省略function makeFieldLens<T, K = keyof T>(key: K): Lens<T, T[K]> {
return {
get: (container: T): T[K] => container[key],
set: (container: T, value: T[K]): T => ({ ...container, [key]: value }),
};
}
// This won't typecheck, because the return is `number | string` not `number`.
const valueLens = makeFieldLens<Container>('value');
const value: number = valueLens.get(container);
的类型,以便编译器仅将key
推导为typeof key
,将any
的返回类型也推导为get
突出any
:
number
Typescript镜头库将关键方法放在一个类中:
function makeFieldLens<T>(key): Lens<T, T[typeof key]> {
return {
get: (container: T): T[typeof key] => container[key],
set: (container: T, value: T[typeof key]): T => ({ ...container, [key]: value }),
};
}
const valueLens = makeFieldLens<Container, 'value'>('value');
const value: number = valueLens.get(container);
// This won't fail, because the return type is `any` not `string`.
const otherLens = makeFieldLens<Container>('other');
const other: number = otherLens.get(container);
但这没用,因为我没有外部类。我尝试了psudo复制它,但是无法实例化以下内容:
class Lens<T>
{
makeFieldLens: <K extends keyof T>(key: K): T[K];
}
我尝试使用第二个函数,推断第一个函数的类型并将其显式传递给第二个:
interface FieldLens<T> {
<K extends keyof T>(key: K): T[K];
};
那仍然推断function keyLensInternal<T, K extends keyof T>(key: K): Lens<T, T[K]> {
return {
get_: (container: T): T[K] => container[key],
set_: (container: T) => (value: AddDateString<NonNullable<T[K]>>): T => ({ ...container, [key]: value }),
};
}
export function keyLens<T>(key) {
return keyLensInternal<T, typeof key>(key);
}
为返回类型,并且对这些类型非常聪明无法编译:
any
一个基本问题是,大多数解决方案推断export function keyLens<T>(key): ReturnType<typeof (keyLensInternal<T, typeof key>)> {
return keyLensInternal<T, typeof key>(key);
}
或any
而不是keyof T
来仅隔离一个密钥。还有其他方法可以保持良好的API吗?我不介意指定K extends keyof T
(实际上是必须这样做的),但是我想推断<T>
。
答案 0 :(得分:2)
您可以使用“ curried function”来拆分通用参数(因此可以显式指定第一个参数,并推断第二个参数):
const makeFieldLens = <T>() => <K extends keyof T>(key: K): Lens<T, T[K]> => ({
get: (container: T): T[K] => container[key],
set: (container: T, value: T[K]): T => ({ ...container, [key]: value }),
});
const valueLens = makeFieldLens<Container>()('value');