Reputation: 760
I'm using objects in TypeScript that can potentially contain a lazy link where the data can be fetched or the data itself. That's why these properties get a union type T | string.
Now I want to write a type safe resolve function to resolve nested properties that returns the nested type.
type TypeOrString<T> = T | string;
interface A {
propA: TypeOrString<B>;
}
interface B {
propB: TypeOrString<any[]>;
}
function resolve<T, K1 extends keyof T, K2 extends keyof T[K1]>
(data: T | string, p1: K1, p2: K2): T[K1][K2] {
return null;
}
let a:A;
let b:TypeOrString<any[]> = resolve(a, "propA", "propB");
//Error TS2345: Argument of type '"propB"' is not assignable
//to parameter of type '"toString" | "valueOf"'.
But the compiler gives me an error, TS2345: Argument of type '"propB"' is not assignable to parameter of type '"toString" | "valueOf"'. Is there any chance to infer the type of propB from an instance of interface A?
Upvotes: 0
Views: 2083
Reputation: 328618
You can get kind of what you're looking for by using recursive mapped types to model the fact that not only is something "data or a string", but that the data is itself "data or a string". Like this:
type DeepTypeOrString<T> = string | {
[K in keyof T]: DeepTypeOrString<T[K]>
}
This mostly gives you what you want for primitives and plain object types. Arrays are another story, but that will be cleared up once the conditional types feature lands in TypeScript 2.8.
Now let's define A
and B
by the actual data you expect them to be as opposed to the "maybe string" version:
interface A {
propA: B
}
interface B {
propB: any[]
}
And your resolve()
function is similar to before, except that data
is declared to be of type DeepTypeOrString<T>
:
declare function resolve<T, K1 extends keyof T, K2 extends keyof T[K1]>(
data: DeepTypeOrString<T>, p1: K1, p2: K2
): T[K1][K2];
Now when you call resolve()
, you will see the inference you expect:
declare const a: DeepTypeOrString<A>;
const ret = resolve(a, 'propA', 'propB') // ret is inferred as any[]
Hope that helps; good luck!
Upvotes: 4