简体   繁体   English

从父 object 推断窄类型在 typescript 中引用自身

[英]Infer narrow type from parent object referencing itself in typescript

I would like to constrain a parameter type of a callback function that is inside an object like this:我想像这样约束 object 中的回调 function 的参数类型:

makeObj({
  items: [
    {
      value: "foo",
      func(items) {}
    },
    {
      value: "bar",
      func(items) {}
    }
  ]
});

I want to constrain the parameter items of func to "foo" | "bar"我想将func的参数items约束为"foo" | "bar" "foo" | "bar" . "foo" | "bar" The values of value can be any string though. value 的value可以是任何字符串。

I already have this, but it obviously doesn't work:我已经有了这个,但它显然不起作用:

interface MyObject<T extends string, Values extends readonly MyObject<T, Values>[]> {
  value: T;
  func: (items: Values) => void;
}
interface Items<T extends string, Data extends readonly MyObject<T, Data>[]> {
  items: readonly [...Data];
}
function makeObj<T extends string, Data extends readonly MyObject<T, Data>[]>(opt: Items<T, Data>) {
  return opt;
}

makeObj({
  items: [
    {
      value: "foo",
      func(items) {}
/* Error on "func":
Type '(items: MyObject<string, unknown>) => void' is not assignable to type '(items: unknown) => void'.
  Types of parameters 'items' and 'items' are incompatible.
    Type 'unknown' is not assignable to type 'MyObject<string, unknown>'.
*/
    },
    {
      value: "bar",
      func(items) {} // same error on "func"
    }
  ]
});

Typescript Playground Typescript 游乐场


Some background info: I'm writing a program that has some "actions" defined.一些背景信息:我正在编写一个程序,其中定义了一些“动作”。 Each action is a function in an object to describe the function (give it a name, description, input type, output type, secondary input type).每个action都是一个function在一个object来描述function(给它一个名字,描述,输入类型,output类型,辅助输入类型)。 The secondary input relies on user provided data.辅助输入依赖于用户提供的数据。 Each item of this data has a name and defined type.此数据的每一项都有一个名称和定义的类型。 Its value is provided by the end user.它的值由最终用户提供。 Depending on the value of other items an item can be displayed to the user or not.根据其他项目的价值,一个项目是否可以显示给用户。 This last part is controlled by the callback function in question.最后一部分由相关回调 function 控制。
I have all this already set up with narrow type inference to not make any mistakes while writing each action but this narrow type inference breaks down regardless of which type the callback function has.我已经使用窄类型推断设置了所有这些,以便在编写每个操作时不会犯任何错误,但是无论回调 function 具有哪种类型,这种窄类型推断都会失效。 Either because it can't infer the narrow type anymore, or because I get a type error (eg when I use object as type for the parameter (I ultimately want to use an object as the parameter, not just a string)).要么是因为它不能再推断出窄类型,要么是因为我收到类型错误(例如,当我使用object作为参数类型时(我最终想使用 object 作为参数,而不仅仅是一个字符串))。 This narrow type inference works like this: typescript - Infer/narrow function argument from sibling property这种窄类型推断是这样工作的: typescript - Infer/narrow function argument from sibling property


Edit:编辑:

I would like a solution where I don't have the type embedded in the function parameter but as an interface/type that I can reference like:我想要一个解决方案,其中我没有在 function 参数中嵌入类型,但作为我可以引用的接口/类型,例如:

interface Magic<MagicParam> {
  items: MagicParam;
}
makeObj<MagicParam>(opt: Magic<MagicParam>) {
  return opt;
}

Magic can be an interface or type and can have any number of type parameters. Magic可以是接口或类型,并且可以有任意数量的类型参数。 makeObj too can have any number of type parameters. makeObj也可以有任意数量的类型参数。

Your makeObj() function could be generic in the union of string literal types corresponding to the value properties of the function parameter's items property.您的makeObj() function 在与 function 参数的items属性的value属性相对应的字符串文字类型联合中可以是通用的。 If we call that generic type parameter K , and if you pass in, for example, {items: [{value: "x"},{value: "y"},{value: "z"}]} (ignoring func for now), then K should be the "x" | "y" | "z"如果我们调用该通用类型参数K ,并且如果您传入,例如{items: [{value: "x"},{value: "y"},{value: "z"}]} (忽略func现在),那么K应该是"x" | "y" | "z" "x" | "y" | "z"

Then you can express the argument to makeObj() in terms of K .然后您可以根据K表达makeObj()的参数。 If we call that type Opt<K> , we can write it like this:如果我们调用该类型Opt<K> ,我们可以这样写:

interface Opt<K extends string> {
  items: Array<{ value: K, func: (items: K) => void }>
}

That means, given K of value properties, we want the items property of Opt<K> to be an array, where the elements of the array are objects with a value property of type K , and a func callback property whose input is type K .这意味着,给定Kvalue属性,我们希望Opt<K>items属性是一个数组,其中数组的元素是具有类型Kvalue属性的对象,以及输入类型为Kfunc回调属性.

Let's make sure that's what you want, by evaluating Opt<"foo" | "bar">让我们通过评估Opt<"foo" | "bar">来确保这就是您想要的。 Opt<"foo" | "bar"> (and using conditional type inference and a mapped type to coax the compiler into displaying details of the structure): Opt<"foo" | "bar"> (并使用条件类型推断映射类型来诱使编译器显示结构的详细信息):

type Test = Opt<"foo" | "bar"> extends
  infer O ? { [K in keyof O]: O[K] } : never 

/* type Test = {
    items: {
        value: "foo" | "bar";
        func: (items: "foo" | "bar") => void;
    }[];
} */

Looks good.看起来挺好的。


Anyway, as mentioned above, makeObj will be generic in K and take a parameter of type Opt<K> :无论如何,如上所述, makeObjK中将是通用的,并采用Opt<K>类型的参数:

function makeObj<K extends string>(opt: Opt<K>) {
  return opt;
}

And let's see if it works:让我们看看它是否有效:

const obj = makeObj({
  items: [
    {
      value: "foo",
      func(items) {
        // (parameter) items: "foo" | "bar"
      }
    },
    {
      value: "bar",
      func(items) {
        // (parameter) items: "foo" | "bar"
      }
    }
  ]
});
// const obj: Opt<"foo" | "bar">

Looks good too.看起来也不错。 The compiler infers that K is "foo" | "bar"编译器推断K"foo" | "bar" "foo" | "bar" , and then contextually types the items callback parameters of the func property to also be "foo" | "bar" "foo" | "bar" ,然后根据上下文func属性的items回调参数键入为"foo" | "bar" "foo" | "bar" . "foo" | "bar" And the obj that comes out is of the desired Opt<"foo" | "bar">出来的obj是所需的Opt<"foo" | "bar"> Opt<"foo" | "bar"> type. Opt<"foo" | "bar">类型。

Playground link to code 游乐场代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM