Chris
Chris

Reputation: 75

Constrain Typescript generics to object

Hi guys,

I'm not that familiar with Typescript, but here is the following I want to achieve, and I'm currently struggling with:

Create an object with the function createObject as defined below. As argument you can pass in only a parameter of type Object.

It returns the passed in argument, but modified by an type and detail property).

This is, what I have done so far.

function createObject<T extends object>(options: T): T {
  if (!options.type) {
    if (options.showDetail) {
      options.type = 'text';
      options.detail = 'Some detail';
    } else {
      options.type = 'simple';
      options.detail = '';
    }
  }
  return options
}

When I execute my function:

const objectConfig = {
  name: 'Steven',
  age: 28,
}

const newObj = createObject(objectConfig);

Inside the function createObject, options.type, options.showDetail, options.detail are highlighted red. Typescript complains about:

[Line 4] Property 'type' does not exist on type 'T'.

[Line 3] Property 'showDetail' does not exist on type 'T'.

[Line 5, 8] Property 'detail' does not exist on type 'T'.

Furthermore, returning the Generic, won't return the modified argument, instead just the input argument. I.e. I won't get Typescript hints about the detail/type property of newObj.

My IDE just gives me type hints for the age and name property. As expected since I return erroneously just the input argument.

How can I get a proper typescript solution? Thank you!

Upvotes: 1

Views: 108

Answers (1)

Devansh J
Devansh J

Reputation: 4194

Well... The problem is you are writing TypeScript as if it is just like JavaScript. That's not how it works. Here's a solution that just works but you need to start writing TypeScript like you write any other OOP language1.

interface OptionExtensions {
    type?: string,
    showDetail?: boolean,
    detail?: string
}

function createObject<T extends object>(options: T): T & OptionExtensions {
    let optionsExtended: T & OptionExtensions = options;
    if (!optionsExtended.type) {
        if (optionsExtended.showDetail) {
            optionsExtended.type = 'text';
            optionsExtended.detail = 'Some detail';
        } else {
            optionsExtended.type = 'simple';
            optionsExtended.detail = '';
        }
    }
    return optionsExtended;
}

let foo = createObject({
    name: "Steven",
    age: 28
});

Here's a demo. You can check foo has all properties that you would expect (by writing "foo." and seeing the suggestions).

Footnote 1: By this I mean because TypeScript has nice type inference and duck typing it will allow you to make things work without explicitly writing types and making interfaces but not for too long. Think how would you write things in a OOP language and then make appropriate abstractions and models.

Upvotes: 2

Related Questions