如何在 Typescript 查找中匹配键和值

How to match key and value in a Typescript lookup

更新:

感谢@OliverRadini 让我到达那里。根据他们的回复,我有一个适用于我的案例的解决方案。我对他们的回答遇到的唯一问题是,我需要在外部键入您传递给函数的内容,但无法想出一种方法来推断密钥,而不是在函数中。在我的例子中,使用 util 为我们做这件事很好,如下所示:

interface ITest {
  n: number;
  b: boolean;
}

const f = <K extends keyof ITest>(x: { id: K; params: ITest[K] }) => ({
  ...x,
  _propToConfirmYourUsingTheUtilNeverWriteThisManually: true,
});

type Props = ReturnType<typeof f>;

const a: Props = f({ id: "n", params: 1 }); // no type error
const b: Props = f({ id: "n", params: true }); // Type 'true' is not assignable to type 'number'

const c = f({ id: "b", params: true }); // no type error
const d = f({ id: "b", params: 1 }); // Type 'number' is not assignable to type 'boolean'

// Errors because we didn't add _propToConfirmYourUsingTheUtilNeverWriteThisManually
// This ridiculously named prop should ensure we don't override it without noticing
const z: Props = {
  // Property '_propToConfirmYourUsingTheUtilNeverWriteThisManually' is missing in type ...
  id: "b",
  params: 7,
};

f(a); // no type error
f(b); // no type error
f(c); // no type error
f(d); // no type error


更新:

这个案子通过就好了:

const invalidProps1: Props<IdsWithParameters> = {
  id: "id1",
  params: {
    param1: "string",
  },
};

额外的属性不是这个之外的代码的问题。但是为了使这个组件函数能够通用并在 IdsWithParameters 中获取任何 id,我们不能像 @oliverradini 回复中那样明确指定密钥,如 Props<'id1'>。但是他们的帮助使我接近了这个目标,但还没有完全实现。

interface Props<K extends keyof IdsWithParameters> {
  id: K;
  params: IdsWithParameters[K];
}

// Somehow want Props to know what is being used as the id and infer 
// the correct params type
function Text(props: Props) {
  const text = intlFunctionThatGetsTheTranslatedText(props.id, props.params);

  return <>{text}</>
}


原版POST:

我正在构建一个库,我需要在其中创建一个对象(IdsWithParameters 接口),指定允许的键以及允许与每个特定键一起使用的类型化参数。在不同的地方,他们会想要指定一个对象,该对象具有 idparams(Props 接口)匹配来自 IdsWithParameters 实例的 1 个特定 key/value 对。

我可以使用查找到达一个点,您必须将有效的键和参数传递给 Props 实例。但无法弄清楚如何将参数限制为与给定 key/id.

匹配的参数
interface IdsWithParameters {
  id1: {};
  id2: {
    param1: string;
  };
  id3: {
    param2: boolean;
    param3: number;
  };
}

interface Props<I extends IdsWithParameters> {
  id: keyof I;
  params: I[keyof I];
}

// This works as expected
const validProps1: Props<IdsWithParameters> = {
  id: "id1",
  params: {},
};

// This works as expected
const validProps2: Props<IdsWithParameters> = {
  id: "id2",
  params: {
    param1: "string",
  },
};

const invalidProps1: Props<IdsWithParameters> = {
  id: "id1",
  // Want this to error, saying that params should be {} but it passes
  params: {
    param1: "string",
  },
};

const invalidProps2: Props<IdsWithParameters> = {
  id: "id2",
  // Want this to error, saying that params should be { param1: string; } but it passes
  params: {},
};

我觉得我已经看这个太久了,要么有一种简单的方法,要么有更好的解决方案,但我再也看不到了。对于上下文,我正在构建一个包来处理 React 包中的可翻译内容字符串。所以它可以像这样使用:

// IdsWithParameters in above example
interface ContentStringsWithAcceptableParameters {
  "errors.404.title": {},
  "errors.404.content": {
     pageUrl: string;
  },
  "errors.retryAttemptsLeft": {
    attemptsLeft: number;
  }
}

interface Props<I extends ContentStringsWithAcceptableParameters> {
  id: keyof I;
  params: I[keyof I];
}

// The react component we'll be using
function Text(props: Props<ContentStringsWithAcceptableParameters>) {
  const text = intlFunctionThatGetsTheTranslatedText(props.id, props.params);

  return <>{text}</>
}

如果我们稍微简化一下示例,我认为我们可以理解我们正在尝试做什么:

interface ITest {
  n: number;
  b: boolean;
}

const f = <K extends keyof ITest>(
  x: {
    id: K,
    params: ITest[K],
  },
) => true;

f({ id: 'n', params: 1 });    // no type error
f({ id: 'n', params: true }); // Type 'true' is not assignable to type 'number'

f({ id: 'b', params: true }); // no type error
f({ id: 'b', params: 1 });    // Type 'number' is not assignable to type 'boolean'

我们可以指定一些泛型(上例中的K)扩展一个类型的键。然后我们可以使用该键来获取我们正在寻找的类型。