Peter Briers
Peter Briers

Reputation: 192

Typescript: Enforce inner type, but return actual type

I have the following code:

class ValueGetter {
  getValues() {
    return {
      value1: this.getValueAsBoolean(somewhere.something),
      value2: this.getValueAsBoolean(somewhere.somethingElse)
    }
  }
}

Typescript knows the exact return-type of the function, which is.

{
  value1: boolean,
  value2: boolean
}

What's really handy that I can even do the following somewhere else:

class MyFoo {
  myBar: ReturnType<ValueGetter['getValues']>
}

Now what I want is to enforce getValues so it can only return the following type {[key: string]: boolean}

But if I add this as the return-type of getValues(), I loose the exact return-type with named keys which Typescript deduced for me.

Is there a way to enforce this typing only for the 'inside' of getValues, without loosing the cool deducement-magic with the 'named keys'-returntype Typescript can do?

Upvotes: 1

Views: 60

Answers (2)

Devansh J
Devansh J

Reputation: 4194

Even though this question is answered here's a better way of doing what you want without adding any runtime overhead:

type HasType<T extends C, C> = T;
function getValues() {
    let foo = {
        value1: Boolean(100),
        value2: 123
    };
    return <HasType<typeof foo, Record<string, boolean>>>foo;
    //              ^^^^^^^^^^ error here
}

Here's a demo

(Also consider marking this as accepted if you think this is better)

Upvotes: 2

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 250056

This is a general problem of wanting to enforce that a value satisfies a particular constraint but at the same time keep the actual type of the value.

Unfortunately typescript does not support any special syntax for this. The best you can do is have a function that has a type parameter with a constraint. The type will be inferred from actual value passed in but it will be checked against the constraint:

class ValueGetter {
    getValues() {
        return constrainToBooleanValues({
            value1: true,
            value2: false,
            worngValue: 1// err
        })
    }
}

function constrainToBooleanValues<T extends Record<string, boolean>>(o: T) {
    return o;
}

You could even generalize the function:

class ValueGetter {
    getValues() {
        return constrain<Record<string, boolean>>()({
            value1: true,
            value2: false,
            worngValue: 1// err
        })
    }
}

function constrain<C>() {
    return <T extends Record<string, boolean>>(o: T) => o;
}

Upvotes: 1

Related Questions