Reputation: 6249
In my code I'm working with my interface that extends an interface of an external library (in order to keep type checks and well, the entire reason for using TypeScript). And generally this works great.
I've now run into a problem where a function from the 3rd party library returns an instance of that base interface, and I need to store it in a variable of my own custom interface, and fill in the remaining properties.
The image with the code and the errors:
(Here is a link to the text version.)
Obviously this problem can be solved by using duck typing and casting, but that feels like it defeats the point of TS altogether, so first thing I'm wondering if there's a syntax to do this.
I've tried:
return { externalFactory(); extraProperty: '' }; // And with comma instead of semi-colon
return externalFactory(): { extraProperty: '' };
return { extraProperty: '' } extends externalFactory();
But my guesses so far have failed. So I'm wondering, is there a syntax or method to do this without losing type safety checks?
You may think extending an object like this would require extra generated code (like extending classes does), but at most it'd be a temporary variable to hold the original object and then add the other properties like:
var temp = externalFactory();
temp.extraProperty = '';
return temp;
And if you're assigning to a variable, that temporary variable isn't even necessary. So it's definitely possible.
Upvotes: 3
Views: 4109
Reputation: 31
Just found out Object.assign works perfect for this, as answered in related topic.
function myTypeFactoryAttempt2() : IMyType {
const theValue: IMyType = Object.assign(
externalFactory(),
{
extraProperty: ''
}
);
return theValue;
}
This combines advantages of both Mike's and Ryan's answers. So this checks all extra fields are actually provided, while extends original object instead of creating new one.
Upvotes: 0
Reputation: 1375
Many years passed, but it seems there is a solution with correct type validation:
function myTypeFactoryAttempt2() : IMyType {
var theValue = externalFactory(); //ExternalType: {someProperty:''}
return { ...theValue, extraProperty: ''};
}
in original accepted solution you have to ensure that you have set all fields for IMyType type.
function myTypeFactoryAttempt3() : IMyType {
var theValue = <IMyType>externalFactory();
return theValue; //incorrect type, but no warning
}
similar code in Playground
Upvotes: 0
Reputation: 220954
This scenario is the very raison d'être of casting (or 'type assertions' as we call them in TypeScript): Someone gives you an expression of type T, and you actually want to treat it as a subtype of T instead.
function myTypeFactoryAttempt2() : IMyType {
var theValue = <IMyType>externalFactory();
theValue.extraProperty = '';
return theValue;
}
In this case you're not actually losing any type safety because the type assertion on the second line is itself typechecked -- if you tried to write <number>externalFactory()
, you would still get a compile error because the asserted type (number) is not a subtype of the expression (IExternalType).
Upvotes: 2