Reputation: 19690
Is there a way to make a typed object literal directly?
By directly I mean without having to assign it to a variable which is type annotated.
For example, I know I can do it like this:
export interface BaseInfo { value: number; }
export interface MyInfo extends BaseInfo { name: string; }
function testA(): BaseInfo = {
const result: MyInfo = { value: 1, name: 'Hey!' };
return result;
}
I also can do it like this:
function testB(): BaseInfo = {
return { value: 1, name: 'Hey!' };
}
But what I need is something like:
function testC(): BaseInfo = {
return { value: 1, name: 'Hey!' }: MyInfo; // <--- doesn't work
}
Or like this:
function testD(): BaseInfo = {
return MyInfo: { value: 1, name: 'Hey!' }; // <--- doesn't work
}
Upvotes: 30
Views: 23322
Reputation: 19690
ORIGINAL ANSWER is to use the identity function:
function to<T>(value: T): T { return value; }
const instance = to<MyInfo>({
value: 1,
name: 'Hey!',
});
there should be no performance impact for an unnecessary call the to
function, it should be optimized away by the JIT compiler
UPDATED ANSWER on Sept 22, 2022:
At last TypeScript team made a tool for it:
const instance = { value: 1, name: 'Hey!' } satisfies MyInfo;
Read more: https://github.com/microsoft/TypeScript/issues/47920
Some screenshots:
Upvotes: 23
Reputation: 19381
To be really type-safe there are these ways:
Example interface:
interface IFoo {
firstName: string;
lastName: string;
}
specify the function result type
function useResultType(): IFoo {
return {
firstName: 'mars'
// compile error, when lastName is missing
// , lastName: 'xx'
};
}
return a constant
function resultVariable() {
const result: IFoo = {
firstName: 'mars'
// compile error, when lastName is missing
// , lastName: 'xx'
};
return result;
}
use a generic identity function (note: rxjs has an identity function)
function ident<T>(value: T): T {
return value;
}
function identityFunction() {
return ident<IFoo>({
firstName: 'mars'
// compile error, when lastName is missing
// , lastName: 'xx'
});
}
when you just use type assertions - the compiler will not show an error when you return any other type (e.g. in the examples, I "forgot" the lastName
member)
function asCast() {
return {
firstName: 'mars'
// NO error
} as IFoo;
}
function cast() {
return <IFoo>{
firstName: 'mars'
// NO error
};
}
you can check the examples in the Typescript Playground
Upvotes: 2
Reputation: 19879
Remember that that interfaces follow duck typing: if an object looks like it matches the interface, it does match the interface.
So
function testB(): IBaseInfo = {
return { name: 'Hey!' };
}
is exactly the same as
function testA(): IBaseInfo = {
var result: IMyInfo = { name: 'Hey!' };
return result;
}
Either way, the returned object looks like an IMyInfo, so it is an IMyInfo. Nothing that happens inside the function affects what interfaces it matches.
However, in your examples, the return value of the function is IBaseInfo, so the compiler and intellisense will assume that the object is just an IBaseInfo. if you want the caller of the function to know that the return value is an IMyInfo, you need to make the return value of the function IMyInfo:
function testB(): IMyInfo = {
return { name: 'Hey!' };
}
or using type inference, simply
function testB() = {
return { name: 'Hey!' };
}
Upvotes: 2
Reputation: 220964
Intellisense for members of object literals is provided by the contextual type (see section 4.19 of the spec) of the expression.
You can acquire a contextual type in a variety of ways. Some of the most common places where a contextual type is applied are:
return
statement in a function or getter with a return type annotation<T>expr
)In your example, you can use a type assertion to force your object literal to have a contextual type:
function testB() {
return <IMyInfo>{ name: 'Hey!' };
}
Upvotes: 20
Reputation: 5203
Your first and second examples fail, so yo can't do that. :)
Pretty sure you don't have to specify any types on your literal. As long as your literal meets the interface requirements, you're good.
interface IMyInfo { name: string; }
var asdf = {
name: "test"
}
var qwer: IMyInfo = asdf;
If you want intellisense, you have to do something like:
Or maybe this is what you're looking for. Intellisense works here, at least on the playground.
Or maybe this. :)
Upvotes: 1