Trident D'Gao
Trident D'Gao

Reputation: 19690

How can I specify a typed object literal in TypeScript?

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

Answers (5)

Trident D&#39;Gao
Trident D&#39;Gao

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:

enter image description here

Upvotes: 23

TmTron
TmTron

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

David Norman
David Norman

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

Ryan Cavanaugh
Ryan Cavanaugh

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:

  • The initializer of a variable with a type annotation
  • The expression in a return statement in a function or getter with a return type annotation
  • The expression in a type assertion expression (<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

Alex Dresko
Alex Dresko

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:

enter image description here

Or maybe this is what you're looking for. Intellisense works here, at least on the playground.

enter image description here

Or maybe this. :)

enter image description here

Upvotes: 1

Related Questions