How to type a standalone object in TypeScript?

Viewed 289
AWSMock.mock("SQS", "receiveMessage", {
    Messages: [
        {
            MessageId: "abcd",
            Body: "sample body",
        },
    ],
});

In the example above, how to type the object as a ReceiveMessageResult instance?

I cannot type the AWSMock.mock() method as this is a generic method in a 3rd party mocking library.

️ This doesn't work, as it wouldn't catch any type error (for example any missing field would not trigger an error):

AWSMock.mock("SQS", "receiveMessage", {
    Messages: [
        {
            MessageId: "abcd",
            Body: "sample body",
        },
    ],
} as ReceiveMessageResult);

️ This could work, but I don't want to go through an extra variable:

const response: ReceiveMessageResult = {
    Messages: [
        {
            MessageId: "abcd",
            Body: "sample body",
        },
    ],
};
AWSMock.mock("SQS", "receiveMessage", response);

Is there any way to type a object on the fly?

3 Answers

I can think of a few options:

  1. Create a PR in the target project with better typing. But I see that there is already a PR from 2019 and it is not merged.
  2. Do not use poorly maintained 3rd party libraries.
  3. Override typings. See this answer. But then you have to maintain it yourself.
  4. Use a variable as you have suggested (which might be the most straightforward option imho)
  5. Create a wrapper function:
function t<T>(input: T): T {
  return input;
}

interface Book {
  id: number;
  title: string;
}

function log(input: any): any {
  console.log(input);
}

log({title: 'Galaxy Guide'} as Book); // Does not complain
log(t<Book>({title: 'Galaxy Guide'})); // Complains that id is missing
  1. Create an overloading function and use it everywhere:
function myAwsMock<T>(service: string, method: string, replace: T): void {
  AWSMock.mock(service, method, replace);
}

First of all, I believe overloads for mock functions should be reordered, but it is not directly related to our problem. I made a PR

In order to make it work, you should write your own custom declaration of aws-sdk-mock module.

  1. Create typings.d.ts file in your root folder.
  2. Paste this code into typings.d.ts
declare module 'aws-sdk-mock' {
    function mock<T>(service: "SQS", method: "receiveMessage", replace: T): void;
}

It is important to add generic T, because it is more specific than any in built in declaration.

  1. Then you should explicitly use generic parameter:

import AWSMock from 'aws-sdk-mock';

type Message = {
    MessageId: string;
    Body: string;
}

type ReceiveMessageResult = {
    Messages: Message[],
}

AWSMock.mock<ReceiveMessageResult>("SQS", "receiveMessage", {
    Messages: [
        {
            MessageId: "abcd",
            Body: "sample body",
            x: 1 // expected error
        },
    ],
});

Voila, it works.

I have tried to make it work without explicit generic parameter, but because there is any type for third argument in built in types, it is impossible to use more specific type after any.

It isn't possible.

My question was about doing this in TypeScript without workarounds (which I already have). All other questions provide new and different workarounds.

I believe these other answers are still useful (feel free to read them), but I want to mark a clear answer to those facing the same problem as me: there is no native solution. It just cannot be done without workarounds.

Related