Is there a way to define only a "part" of the object type, while having the rest of it essentially be "any" type? The goal main goal here is to have intelliSense support for the part that is defeined, but if types-checking support is included, all the better. To illustrate, lets first define one helper type:
type TupleToIntersection<T extends any[]> = {
[I in keyof T]: (x: T[I]) => void
}[number] extends (x: infer U) => void
? U
: never
It does what the name implies, now let’s deal with the issue
type A = {
A: { name: "Foo" }
}
type B = {
B: { name: "Bar" }
}
type Collection<T extends any[]> = TupleToIntersection<{ [I in keyof T]: T[I] }>
declare const C: Collection<[A, B]>
C.A.name // "Foo" as expected
C.B.name // "Bar" as expected
declare const D: Collection<[A, any]>
D // <=== this is now of any type because obviously an intersection of any and type A gives back any
D.A.name // <=== So whilst this is technically still okay, there is no "intelliSense" support
Any way to achieve this?
One idea I had was to keep the type as "any" and then have a typeguard to force it into a type that I know the shape of, where I am using it in code, which I think is the way that tsc actually wants to keep it as a "typesafe" code, but this requires "unnecessary" defning of a typeguard as well as defining types where you need to use them instead of having them defined "for you" by the system
>Solution :
The intersection is similar to the mathematical set intersection, where the result of A & B are all elements from A and B.
any is a set that contains every possible value so intersecting with any will give any at the end:
// any
type Case1 = number & any;
// any
type Case2 = {a: string} & any;
To fix it you can replace any with unknown, which is similar to any, however, the compiler will give preference to the type from the other set:
// number
type Case1 = number & unknown
// {a: string}
type Case2 = {a: string} & unknown;
Testing:
declare const D: Collection<[A, unknown]>;
D; // A
D.A.name // "Foo"
But if you want to support something extra to be added to the type, which I encourage you to avoid, you can use something more specific rather than any or unknown. For instance, to support adding new fields to an object one can use Record<string, any>
Testing:
declare const D: Collection<[A, Record<string, any>]>;
D; // A & Record<string, any
D.A.name; // "Foo"
D.additional = ''; // no error