Closed AmitDigga closed 6 years ago
Partial
is built-in for convenience, but it's trivial to implement yourself. It doesn't have any special or hard-coded rules. So you could, in theory, produce your PathIn
type yourself.
However, there isn't currently any way to modify tuples as types. So you can write a type that finds ['engine']
, and notices that Car['engine']
is an object, and then recurses and finds ['name']
(well, at least an interface that does that; see below), but no way to join ['engine']
and ['name']
to be ['engine', 'name']
.
But if you were willing to accept other structures for representing this path, that wouldn't be a problem. For example, a linked list would be easy enough to do.
The problem then is that Typescript does not support types that circularly reference themselves. Classes and interfaces can do it, and that's the recommended workaround, but a single interface cannot somehow be a union of anything. Types support distribution across a union, which you need for this.
Given the advent of conditional types, perhaps the eager resolution of types, and therefore their inability to handle circular references, should be revisited.
I think things like Car[pathToBodyColor]
where pathToBodyColor
is a tuple is abusing index syntax too much IMO. Anyway, here is a PathIn<T>
type I had a go at.
The best I can do; expect this to blow up:
type Append<X, T extends any[]> =
T extends [infer A0, infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, infer A7, infer A8, infer A9] ?
[A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, X] :
T extends [infer A0, infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, infer A7, infer A8] ?
[A0, A1, A2, A3, A4, A5, A6, A7, A8, X] :
T extends [infer A0, infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, infer A7] ?
[A0, A1, A2, A3, A4, A5, A6, A7, X] :
T extends [infer A0, infer A1, infer A2, infer A3, infer A4, infer A5, infer A6] ?
[A0, A1, A2, A3, A4, A5, A6, X] :
T extends [infer A0, infer A1, infer A2, infer A3, infer A4, infer A5] ?
[A0, A1, A2, A3, A4, A5, X] :
T extends [infer A0, infer A1, infer A2, infer A3, infer A4] ?
[A0, A1, A2, A3, A4, X] :
T extends [infer A0, infer A1, infer A2, infer A3] ?
[A0, A1, A2, A3, X] :
T extends [infer A0, infer A1, infer A2] ?
[A0, A1, A2, X] :
T extends [infer A0, infer A1] ?
[A0, A1, X] :
T extends [infer A0] ?
[A0, X] : never
type TraverseTop<T> =
T extends object ?
{ [K in keyof T]: [K] | Traverse<[K],T[K]> }
: never
type Traverse<P extends any[],T> =
T extends object ?
keyof T extends never ? P :
{ [K in keyof T]: P | Traverse<Append<K,P>,T[K]> }
: P
type Flatten10<T> = T extends any[] ? T : T extends object ? Flatten9<T[keyof T]> : T;
type Flatten9<T> = T extends any[] ? T : T extends object ? Flatten8<T[keyof T]> : T;
type Flatten8<T> = T extends any[] ? T : T extends object ? Flatten7<T[keyof T]> : T;
type Flatten7<T> = T extends any[] ? T : T extends object ? Flatten6<T[keyof T]> : T;
type Flatten6<T> = T extends any[] ? T : T extends object ? Flatten5<T[keyof T]> : T;
type Flatten5<T> = T extends any[] ? T : T extends object ? Flatten4<T[keyof T]> : T;
type Flatten4<T> = T extends any[] ? T : T extends object ? Flatten3<T[keyof T]> : T;
type Flatten3<T> = T extends any[] ? T : T extends object ? Flatten2<T[keyof T]> : T;
type Flatten2<T> = T extends any[] ? T : T extends object ? Flatten1<T[keyof T]> : T;
type Flatten1<T> = T extends any[] ? T : T extends object ? Flatten0<T[keyof T]> : T;
type Flatten0<T> = T
interface Car {
engine: {
name: string;
modelNumber: string;
},
body:{
color:string;
shape:{
}
},
}
type PathIn<T> = Flatten10<TraverseTop<T>>;
let path1:PathIn<Car> = ['engine']; //Okay
let path2:PathIn<Car> = ['abc']; //Error
let pathToBodyColor: PathIn<Car> = ['body','color'];
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.
I love typescript wanted to ask if this can be a feature. I searched some issues related to this but could not find any help.
Sorry for my noobness.
For model Car
Current problem
Describing nested or deep typings needs to be improved. Below code is taken from ngrx/store
So
select
method can be used like thiskeyof
restricts us to only one level.My Suggestion
There should be a way to address deep field or nested path in the language itself. I will be discussing
PathIn<T>
type likePartial<T>
, but it can any other thing likepathin
askeyof
etc. (this is beyond my knowledge)let path: PathIn<Car>;
Sopath
should be limited to['engine']
,['engine', 'name']
,['engine', 'modelNumber']
,['body']
,['body', 'color']
and['body', 'shape']
.For single level it may be
'engine'
or'body'
i.e. without array notation. This is just a sugar syntax, not required.Since
PathIn
only means multilevel path so fortype
ofCar[pathToBodyColor]
should bestring
.let car:Car
, one of these should return value atcar.body.color
car[pathToBodyColor] === car.body.color
pathToBodyColor[car] === car.body.color
pathToBodyColor.from(car) === car.body.color
let emptyPath: Path<T> = [];
is not clear.T
innumber
,undefined
,null
,{}
etc. and as discussed in point2
emptyPath.from("abc")
should return"abc"
itself.Path<T>
whereT
is innumber
,undefined
,null
,{}
etc. should not be possible.let path : PathIn<any>
, the path array can be anything which is a possible valid path.And now the above code for
Store.select()
can beExtra
Dealing with object like
then there is no problem, but we know in real world objects are not like that and
keyof
is very limited.