Reputation: 201
We have the following simplified code where we want to have a function using a list of BaseDescription
, where each BaseDescription
is of a different kind.
Whenever something is returned from test, the type information should still contain the original type. In the example code we have type Example
, so we want result
to be of type BaseDescription<Example>
.
type Base = {
foo: string
};
type BaseDescription<T extends Base> = {
bar: String,
baz: () => T
};
const test = (baseDescriptions: BaseDescription<any>[]) => {
return baseDescriptions[0];
};
type Example = Base & {
qux: string
};
const description: BaseDescription<Example> = {
bar: 'bar',
baz: () => ({ foo: 'foo', qux: 'qux' })
};
const result = test([ description ]);
// result is of type BaseDescription<any>
// but we want BaseDescription<Example>
Things already tried:
<? extends Base>
(coming from Java)<Base>
, but then result will have type Base
instead of Example
Is the described behaviour even possible? And if so, how?
EDIT: Thanks for the answers so far! Defenitly makes me understand TS better.
I want to clarify the question a bit more. The idea is that an array of different Types is passed in. I have craeted a new playground
Additions:
type Example = Base & {
qux: string
};
type OtherExample = Base & {
bla: string
};
const description: BaseDescription<Example> = {
bar: 'bar',
baz: () => ({ foo: 'foo', qux: 'qux' })
};
const otherDescription: BaseDescription<OtherExample> = {
bar: 'bar',
baz: () => ({ foo: 'foo', bla: 'qux' })
};
const [ first, second ] = test([ description, otherDescription ]);
I get the feeling that the thing that I want to achieve, will not be possible.
Upvotes: 2
Views: 141
Reputation: 1926
Any widens your type to practically anything, hence the return type of test will be BaseDescription<any>
as TypeScript has no information about the type at compile time. To achieve that the test function allows you to return the type which got passed in, make use of generics.
Especially: <T extends YourInterface>
For TypeScript to infer the type you can use something like this:
const doSomething = <T extends YourParentInterface>(param: T) => param;
interface X extends YourParentInterface { x: string }
const someObjectInstanceOfX: X = { x: "bla", /* ... some other properties coming from the parent interface */ }
doSomething(someObjectInstanceOfX) // => has return type X
You can check the return type inside TypeScript Playground below you'll find the solution (which is the same as in the Playground):
type Base = {
foo: string
};
type BaseDescription<T extends Base> = {
bar: String,
baz: () => T
};
const test = <T extends Base>(baseDescriptions: BaseDescription<T>[]) => {
return baseDescriptions[0];
};
type Example = Base & {
qux: string
};
const description: BaseDescription<Example> = {
bar: 'bar',
baz: () => ({ foo: 'foo', qux: 'qux' })
};
const result = test([ description ]);
Upvotes: 2
Reputation: 33091
@r3dst0rm 's answer is perfectly valid and should be accepted.
If you are interested in alternative way of inferring array elements you can consider next example ([...R]
):
This trick will do the job:
type Base = {
foo: string
};
type BaseDescription<T extends Base> = {
bar: String,
baz: () => T
};
const test = <T extends Base, R extends BaseDescription<T>[]>(baseDescriptions: [...R]) => {
return baseDescriptions[0];
};
type Example = Base & {
qux: string
};
const description: BaseDescription<Example> = {
bar: 'bar',
baz: () => ({ foo: 'foo', qux: 'qux' })
};
const result = test([ description ]); // BaseDescription<Example>
You just need to infer all elements in the array <T extends Base, R extends BaseDescription<T>[]>(baseDescriptions: [...R])
Upvotes: 1