Javascript Typescript递归映射对象属性类型:对象和数组元素类型

Javascript Typescript递归映射对象属性类型:对象和数组元素类型,javascript,reactjs,typescript,data-binding,Javascript,Reactjs,Typescript,Data Binding,更新1->见下文 给定以下接口 interface Model { bla: Child1; bwap: string; } interface Child1 { blu: Child2[]; } interface Child2 { whi: string; } 我正在尝试编写一个函数,它的基本用法如下: let mapper = path<Model>("bla")("blu"); let myPropertyPath = mapper.path

更新1->见下文

给定以下接口

interface Model {
    bla: Child1;
    bwap: string;
}
interface Child1 {
    blu: Child2[];
}
interface Child2 {
    whi: string;
}
我正在尝试编写一个函数,它的基本用法如下:

let mapper = path<Model>("bla")("blu");
let myPropertyPath = mapper.path; //["bla", "blu"]
其中
s
参数为
path()
TState=Model
bind.text
函数将返回一个
value
属性和一个
onChange
处理程序,该处理程序将调用组件的
setState
方法,设置通过映射到新值的路径找到的值

这一切都非常有效,直到您在混合中抛出数组属性。 假设我想为
Child1
界面上的
blu
属性中找到的所有元素呈现输入。我想写的是这样的:

interface IPathResult<TRoot, TProp> {
    <TSubKey extends keyof TProp>(key: TSubKey): IPathResult<TRoot, TProp[TSubKey]>;
    path: string[];
}

function path<TRoot>() {
    return <TKey extends keyof TRoot>(key: TKey) => subpath<TRoot, TRoot, TKey>([], key);
}

function subpath<TRoot, T, TKey extends keyof T>(parent: string[], key: TKey): IPathResult<TRoot, T[TKey]> {
    const newPath = [...parent, key];
    const x = (<TSubKey extends keyof T[TKey]>(subkey: TSubKey) => subpath<TRoot, T[TKey], TSubKey>(newPath, subkey)) as IPathResult<TRoot, T[TKey]>;
    x.path = newPath;
    return x;
}
let mapper = path<Model>("bla"); //IPathResult<Model, Child1>
for(let i = 0; i < 2; i++) { //just assume there are 2 elements in that array
    let myPath = mapper("blu", i)("whi"); //IPathResult<Model, string>, note: we're writing "whi", which is a key of Child2, NOT of Array<Child2>
}
interface IArrayPathResult<TRoot, TProp extends Array<TProp[0]>> {
    <TSubKey extends keyof TProp[0]>(key: TSubKey): IPathResult<TRoot, TProp[0][TSubKey]>;
    path: string[];
}
因此,这就是我被困的地方:即使不考虑实际实现,我如何在
IPathResult
接口中定义重载?以下是它的要点,但不起作用:

interface IPathResult<TRoot, TProp> {
    <TSubKey extends keyof TProp>(key: TSubKey): IPathResult<TRoot, TProp[TSubKey]>;
    <TSubKey extends keyof TProp>(key: TSubKey, index: number): IPathResult<TRoot, TProp[TSubKey][0]>;
    path: string[];
}
这使我能够实际键入以下内容:

const result: IArrayPathResult<Model, Child2[]> = null;
result("whi"); //woohoow!
这不应该起作用,因为
mapper(“bla”)
调用返回的是
IPathResult
,它不可分配给
IPathResult
! 如果在
IPathResult
界面中删除2个方法重载中的一个,则上面的代码不会出现错误,并指出“类型'Child1'不可分配给类型'string'


太近了!还有其他想法吗?

您可以使用条件类型提取item元素,并确保仅使用数组键调用数组版本:

type KeysOfType<T, TProp> = { [P in keyof T]: T[P] extends TProp? P : never}[keyof T];
type ElementTypeIfArray<T> = T extends Array<infer U> ? U : never

interface IPathResult<TRoot, TProp> {
    <TSubKey extends KeysOfType<TProp, Array<any>>>(key: TSubKey, index: number): IPathResult<TRoot, ElementTypeIfArray<TProp[TSubKey]>>;
    <TSubKey extends keyof TProp>(key: TSubKey): IPathResult<TRoot, TProp[TSubKey]>;
    path: string[];
}

let mapper = path<Model>()("bla")("blu", 1)("whi"); // works
let mapper2 = path<Model>()("bla", 1) // error bla not an array key

该死的!还没有很好地浏览新功能!谢谢,伙计,太棒了,正是我想要的!!我说得太快了,请再看看我的帖子“更新1”。太近了!啊,是的,typescript和未使用的泛型在一半的时间里都不能很好地发挥作用,是的:)尽管intellisense确实报告了
IPathResult
被返回,但这仍然很奇怪,因此类型是明确的。如果你发现什么,请告诉我,我也会再看一看。它可能会成为github论坛的类型脚本。在此期间,我将此标记为公认的答案;这个额外的属性真的不会妨碍任何事情:)
const result: IArrayPathResult<Model, Child2[]> = null;
result("whi"); //woohoow!
const mapper = path<Model>();
const test: IPathResult<Model, string> = mapper("bla"); //doesn't error!
type KeysOfType<T, TProp> = { [P in keyof T]: T[P] extends TProp? P : never}[keyof T];
type ElementTypeIfArray<T> = T extends Array<infer U> ? U : never

interface IPathResult<TRoot, TProp> {
    <TSubKey extends KeysOfType<TProp, Array<any>>>(key: TSubKey, index: number): IPathResult<TRoot, ElementTypeIfArray<TProp[TSubKey]>>;
    <TSubKey extends keyof TProp>(key: TSubKey): IPathResult<TRoot, TProp[TSubKey]>;
    path: string[];
}

let mapper = path<Model>()("bla")("blu", 1)("whi"); // works
let mapper2 = path<Model>()("bla", 1) // error bla not an array key
interface IPathResult<TRoot, TProp> {
    <TSubKey extends KeysOfType<TProp, Array<any>>>(key: TSubKey, index: number): IPathResult<TRoot, ElementTypeIfArray<TProp[TSubKey]>>;
    <TSubKey extends keyof TProp>(key: TSubKey): IPathResult<TRoot, TProp[TSubKey]>;
    path: string[];
    __: TProp;
}