Reputation: 720
I'm trying to compose initialization object type which I can use to initialize mock objects. I don't want to define all fields because I'd like to reuse this type. I defined the custom type like this:
type InitObjType = { [key: string]: string } & { customKey?: Observable<boolean> };
function initializator(obj: InitObjType) {...}
if I try to pass the init object into initializator
function like that:
const subject = new Subject<boolean>;
initializator({
a: 'a',
b: 'b',
c: 'c',
customKey: subject,
});
I receive the error:
error TS2345: Argument of type '{ a: string; b: string; c: string; customKey: Observable<boolean>; }' is not assignable to parameter of type 'InitObjType'.
Type '{ a: string; b: string; c: string; customKey: Observable<boolean>; }' is not assignable to type '{ [key: string]: string; }'.
Property 'customKey' is incompatible with index signature.
Type 'Observable<boolean>' is not assignable to type 'string'.
I use TypeScript 3.5.3.
Are there any ideas why intersection of types doesn't work?
Thanks in advance!
Upvotes: 0
Views: 338
Reputation: 5418
There is a good explanation on index types here.
Declaring an index signature basically means that all explicit properties need to conform to the index signature as well. Using an intersection type to workaround this does work when declaring the type - but you will not be able to create the object. There are some discussions about this on github as well.
Using a Subtype
It is considered a best practice to move the index signatures to a subtype like this:
type InitObjType = {
customKey?: Observable<boolean>,
additionalProperties: { [key: string]: string }
};
function initializator(obj: InitObjType) {...}
const subject = new Subject<boolean>;
initializator({
customKey: subject,
additionalProperties: {
a: 'a',
b: 'b',
c: 'c',
}
});
Using less type-safe options
If you absolutely have to keep the additonal properties on the same level, you have to use a less type safe way. Either by changing the index signature:
type InitObjType = {
customKey?: Observable<boolean>,
[key: string]: string | Observable<boolean>
};
// Or even less type-safe with any
type InitObjType = {
customKey?: Observable<boolean>,
[key: string]: any
};
Or by typecasting when creating the object:
const subject = new Subject<boolean>;
initializator({
a: 'a',
b: 'b',
c: 'c',
customKey: subject,
} as any);
This last example has an interesting sideeffect. Since TypeScript only hinders you from creating an object of this type (thus you have to cast it to any), you can still cast that object back to get type safety again (the tradeoff being that you have to type it out each time).
const subject = new Subject<boolean>;
initializator({
a: 'a',
b: 'b',
c: 'c',
customKey: subject,
} as any as InitObjType);
Upvotes: 2