Typescript KeyOf - 数组或对象 - 绝望

Typescript KeyOf - Array or Object - Desperate

我有以下class.......

export class Person<T, C = T> {
    lens: any = null;
    private value: T;

    constructor(value: T, newLens?: any) {
        this.value = value;
        this.lens = newLens;
    }

    at<K extends keyof C>(path: keyof C): Person<T, C[K]> {
        if(!this.lens) {
            return new Person<T, C[K]>(this.value, Lens.lens(path));
        }
        return new Person<T, C[K]>(this.value, Lens.compose(this.lens)(Lens.lens(path)));
    }

    get(): any {
        if(!this.lens) return this.value;
        return Lens.get(this.lens)(this.value);
    }

    set(f: (newValue: any) => any): T {
        return Lens.set(this.lens)(f(this.get()))(this.value);
    }
}

我的问题是,当我尝试在一个对象上使用我的新人 class 时,我得到了不正确的行为......

const TestPerson = {
      name: {
          name: "steve"
      },
      siblings: [{name: "shanon"}]
      age: Infinity
}

const test = new Person(TestPerson).at("name").at("name") // works....
const test2 = new Person(TestPerson).at("siblings").at(0) // fails
const test3 = new Person(TestPerson).at(siblings").at("0") // still fails.
const test4 = new Person(TestPerson).at("nonexistantproperty") //correctly fails.

我的问题是我需要一个 "AT" 可以处理 keyof 对象和 keyof 数组的函数,但似乎无论我如何修改它都无法实现。

对我来说,这似乎是打字稿的一个巨大缺陷,数组和对象都只是引擎盖下的对象,因此对象类型上的 keyof 和数组类型上的 keyof 应该以相同的方式工作。

最简单的解决方案是等到 typescript 2.9(在 RC at the time of writing, should be released soon). Prior to 2.9 keyof only returned string indexes, in 2.9 this will include numeric and symbol keys. See the PR 中)。在 2.9 中,您的代码将按预期工作而无需任何更改。

在 2.8 中,您可以使用条件类型来实现类似的效果

type CReturn<C, K extends keyof C> = C extends Array<any> ? C[number] : C[K];
export class Person<T, C = T> {
    lens: any = null;
    private value: T;

    constructor(value: T, newLens?: any) {
        this.value = value;
        this.lens = newLens;
    }

    at<K extends keyof C>(path: C extends Array<any> ? number : K): Person<T, CReturn<C, K>> {
        if(!this.lens) {
            return new Person<T, CReturn<C, K>>(this.value, Lens.lens(path));
        }
        return new Person<T, CReturn<C, K>>(this.value, Lens.compose(this.lens)(Lens.lens(path)));
    }
}
const TestPerson = {
    name: {
        name: "steve"
    },
    siblings: [{ name: "shanon" }],
    age: Infinity
}

const test = new Person(TestPerson).at("name").at("name") // works....
const test2 = new Person(TestPerson).at("siblings").at(0) // ok
const test21 = new Person(TestPerson).at("siblings").at(0).at("name") //ok
const test4 = new Person(TestPerson).at("nonexistantproperty") //correctly fails.