推断`K扩展了打字稿中的keyof T`,而不仅仅是`any`或`keyof T`

时间:2020-09-22 15:15:10

标签: typescript typescript-generics

我正在写一些镜头功能:

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 }),
  };
}

但是由于不能部分指定模板,因此需要在通用类型参数中同时指定TK,这变得非常麻烦且重复:

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>

1 个答案:

答案 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');

Playground