Gergő Horváth
Gergő Horváth

Reputation: 3705

Can I set possible types of a type argument?

Let's see I want to build a function:

type A = {
  foo: boolean
};
type B = {
  bar: boolean
}

type PossibleObjects = A | B

type Object <T> = {
  [key in keyof T]?: boolean
}

const fn = <
T //:PossibleObjects
>(object: Object<T>) => object;

Even though this function doesn't make anything useful, it's good to explain what I want. I want to control what T can be, in this case A or B.

Is that possible?

Upvotes: 1

Views: 47

Answers (3)

kaya3
kaya3

Reputation: 51152

The keyword extends sets an upper bound on a type argument. This works both on type declarations and on function declarations.

type SomeObject<T extends PossibleObjects> = {
  [key in keyof T]?: boolean
}

const fn = <T extends PossibleObjects>(obj: SomeObject<T>) => obj;

Note that T here isn't constrained to be either A or B; T could also be PossibleObjects itself. I don't think there is a way to prohibit that, but you should be aware of it.

Upvotes: 1

pascalpuetz
pascalpuetz

Reputation: 5428

You can achieve that by narrowing the generic type T to extend your possible Objects.

type A = { foo: boolean };
type B = { bar: boolean };
type PossibleObjects = A | B

type SomeObject <T> = {  // Renamed to "SomeObject" since "Object" is already a default type
  [key in keyof T]?: boolean
}

const fn = <T extends PossibleObjects> // This makes T a subtype of PossibleObjects
   (object: SomeObject<T>) => object;

fn({ foo: true });  // Works
fn({ bar: true });  // Works
fn({ baz: true });  // Error: Argument of type '{ baz: boolean; }' is not assignable to parameter of type 'SomeObject<A> | SomeObject<B>'.

If your SomeObject type can only ever (and not just in your fn function) specify keys that are in your PossibleObjects type, you might want to do this on your SomeObject type as well.

type A = {
  foo: boolean
};
type B = {
  bar: boolean
}

type PossibleObjects = A | B

type SomeObject <T extends PossibleObjects> = { // Adds the type constraint here
  [key in keyof T]?: boolean
}

// Now the following works
const fn = <T extends PossibleObjects>(object: SomeObject<T>) => object;
// But this one will NOT be valid anymore 
const fn2 = <T>(object: SomeObject<T>) => object; 
// Error:                          ^
// Type 'T' does not satisfy the constraint 'PossibleObjects'.

Upvotes: 1

assax24
assax24

Reputation: 171

Use the pipe (|) operator to provide different types:

function fn(arg: A | B): any {
    // code
}

Upvotes: 0

Related Questions