31

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
}
6
  • Could you please explain why you "need something like" the third and fourth examples? Commented Oct 19, 2013 at 1:32
  • @DavidNorman, in order to enable the intellisense autocompletion Commented Oct 19, 2013 at 1:45
  • Where do you want to use intellisense? In the third and fourth examples, there aren't any variables anywhere that you could use intellisense on. In the first example intellisense works on var. Could you expand one of the examples to explain what you want intellisense for? Commented Oct 19, 2013 at 3:03
  • 1
    @DavidNorman, in the 3rd and 4th examples all I want is to construct an instance of IMyInfo and return it immediately. I do need intellisense while working on the content of the object I am returning. So again the question was how can I put an object literal to a context that would impose the type on that object, so that the intellisense is able to help me constructing it. I don't want to involve variables just for the sake of inducing that context, because it would be an unnecessay memory allocation and assignment. Ryan already suggested a nice way of doing it. Commented Oct 19, 2013 at 3:18
  • Note that in Ryan's example, the function returns an IMyInfo, not an IBaseInfo as in your examples. That's why intellisense on the returned value knows about the name property. If, as in your examples, the return value is IBaseInfo, then the intellisense won't know about the name property. Commented Oct 19, 2013 at 3:27

5 Answers 5

24

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

Sign up to request clarification or add additional context in comments.

6 Comments

That actually generates code to call the function. Is there no simpler way to check the type of the object literal?
this code is supposed to be written by hands, not sure what you ask, it's not about checking the type but imposing the type on a literal (otherwise untyped, or more precisely, typed anonymously)
I think @DanDascalescu means that the function will actually be called at runtime when the object literal is created. As you mention this is probably completely negligible (due to likely JIT optimizations or simply that the literal is only created a few times) but still doesn't feel optimal.
Satisfies is not the answer unfortunately - it does not enforce a specific type, it just puts a lower bound on it. The actual inferred type is still the literal you put in there. For instance, if the intended type has an optional property, the inferred type will not have that property and attempting to read it will result in compile error.
Even the example in the image does not compile, as p is locked in as { kind: 'cat', meows: true } and assigning dog is an error.
|
20

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!' };
}

3 Comments

Aleksey's technique is nice when intersection types are involved, since it can ensure required properties of a type with more properties. Example: gist.github.com/brandonbloom/494d46eaf5851646c3eb8733bfaf7035
I prefer this method because it also avoids ambiguity when generating types within lambdas, since the first character is a < rather than a {.
<IMyInfo> { name: 'Hey!' }; is super unsafe, consider changing your answer not to spread bad habits
2

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!' };
}

3 Comments

Thank you, I know about duck typing and type inference. The question is about a diffrent thing though.
Updated to talk about intellisense outside the function.
The example I wrote is simplified version of some code I am working on. I put it this way not to draw the attention away from the things in focus. I can't just return IMyInfo instead of IBaseInfo because the original function returns either an instance of IMyInfo or an instance of another type inherited from IBaseInfo. So the IBaseInfo is the common denominator. As Ryan pointed out you can tell the compiler to use IMyInfo as the type of the result value (even though the function is annotated with IBaseInfo) by using the type assertion operator. I checked it and it worked just as I wanted.
2

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

Comments

1

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

4 Comments

although I don't have to specify it explicitly as long as the type can be inferred I wish I could do that anyway for the sake of having Intellisense support at defining that object literal
you don't get it, I don't want to assign my object to a variable ("something" in your example) in order to have intellisense assistance, what I am asking is if there is a way to annotate the object literal iteself and thus have the intellisense help skipping assigning it to a variable
same thing, can't you see it? you annotated the "asdf" variable, let me help you, try doing it without using the "var" keyword
doesn't work with a type hierarchy anyway, check the updated question, I cannot rely on the type of the return value of the function because it is of the base interface while the actual value being return is of the derived interface

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.