Как переопределить поля объекта и заставить IDE увидеть это? - PullRequest
0 голосов
/ 25 марта 2019

У меня есть некоторый класс с некоторым свойством State, и я форматирую поля State с помощью некоторой функции:

function DoMagic(prototypeObj: any) : any;

Итак, функция "DoMagic" превращает объект из:

const Foo = {
    ID: -1,
    Name: '',
    Something: [1,2,3]
}

to

const Bar = {
    ID: {
        get Value(){return Foo.ID;},
    },
    Name: {
        get Value(){return Foo.ID;},
    },
    Something: {
        0: {get Value(){return Foo.Something[0]} },
        1: {get Value(){return Foo.Something[1]} },
        2: {get Value(){return Foo.Something[2]} },
    }
};

Работает нормально, но я хочу, чтобы автозаполнение WebStorm предлагало свойства Bar вместо Foo, когда Foo является свойством класса.

Если я что-то наподобие

type ClassState = {
    ID: number;
    Name: string;
    Something: number[]
}
class Class1{
    State: ClassState = {
        ID: -1,
        Name: '',
        Something: [1,2,3],
    };
    constructor(){
        Object.defineProperty(this, 'State', DoMagic(this.State))
    }
}
let instance = new Class1();

И здесь нет иллюзий: intellisense показывает мне, конечно, свойства ClassState.

Самый близкий способ, которым я могу это получить - определить некоторые пользовательские типы и объявить собственный «интерфейс» следующим образом:

class TypesBase {
    protected _Value: any;
    get Value():any {return this._Value};
}
class TypeInt extends TypesBase{
    get Value() : number {return this._Value};
}
class TypeString extends TypesBase{
    get Value() : string {return this._Value};
}
class TypeArray<T> extends TypesBase{
    get Value() : T[] {return this._Value};
}
class Class2 {
    State = {
        ID: new TypeInt(),
        Name: new TypeString(),
        Something: new TypeArray<number>(),
    };
    StateData = {
        ID: -1,
        Name: '',
        Something: [1,2,3],
    };
    constructor(){
        Object.defineProperty(this, 'State', DoMagic(this.StateData))
    }
}
let inst2 = new Class2();

Он отлично работает для простых полей, таких как inst2.ID.Value, но inst2.Something[0].Value - нет, и, в любом случае, я хочу, чтобы он работал глубже.

Возможно ли это вообще?

// ДОБАВЛЕНО Спасибо, jcalz, этот способ чище

type Magic<T> = {[K in keyof T]: any | {Value: T[K]}};
class Class2 {
    State: Magic<ClassState> = {
        ID: -1,
        Name: '',
        Something: [1,2,3],
    };
...

Но как заставить его работать на более глубоких уровнях?Как:

inst2.Something[0].Value
inst2.Something[0].DeeperLevelObject.Value

1 Ответ

0 голосов
/ 26 марта 2019

Проблема решена.

Необходимо объявить типы:

type Simple<T> = {Value: T};
type MagicArray<T, U> = {
    [Key: number]: Simple<U>;
    Value: T
}

type Magic<T> = Simple<T>
    &
    {   readonly [K in keyof T]:
            T[K] extends (infer ElementType)[] ? MagicArray<T[K], ElementType>:
            T[K] extends object ? Magic<T[K]> :
            Simple<T[K]>
    }

Использование:

type ClassState2 = {
    Field1: number;
    Field2: string;
};
type ClassState = {
    ID: number;
    Name: string;
    SomeArr: number[];
    SomeObj: ClassState2;
    SomeObjArr: ClassState2[];
};

const SomeState : Magic<ClassState> = null;

//now SomeState can get autocomplete with proper types:
SomeState.Value; 
SomeState.ID.Value;
SomeState.SomeArr[0].Value;
SomeState.SomeObjArr[0].Field1.Value;
...
...