Typescript Conditional Types and Object Properties

Viewed 2901

Is it possible to declare a generic class(or factory function as in the example) in TypeScript, that accepts a binary conditional type variable, that affects a class property type and related functions?

For example:

export function CreateModel<T extends 'CREATE' | 'UPDATE'>(mode: T) {
  type valueType = T extends 'CREATE' ? { name: string } : { name: string; email: string };
  return {
    value: {} as valueType,
    onChange(change: Partial<valueType>) {
      if (mode === 'UPDATE') {
        this.value.email = change.email;
      }
    }
  };
} 
1 Answers

So it looks like you are extending a string with T extends 'CREATE' | 'UPDATE', I'm not sure you can do that. This is basically saying "it can be any string or these two specific strings" - and this is essentially the same as saying "any string"

If you are trying to say you only want the input value to be either 'CREATE' or 'UPDATE' then you can only allow those two strings by doing CreateModel(mode: 'CREATE' | 'UPDATE')

Another issue is that you are using this but you are inside of a function, not a class, so it doesn't work quite the same way and you might have some issues regardless. generally this is discouraged and you should use a class.

Here's what I came up with, and it compiles. I know it kinda goes "around" what your original question was in the first place, but I believe that's also because you aren't using a class here

type ICreateType = { name: string };
type IUpdateType = { name: string; email: string | undefined };
interface IReturnCreateValue {
  value: ICreateType,
};
interface IReturnUpdateValue {
  value: IUpdateType,
  onChange: (change: Partial<IUpdateType>) => void;
};

export function CreateModel(mode: 'CREATE' | 'UPDATE'): IReturnCreateValue | IReturnUpdateValue { 
  if (mode === 'CREATE') {
    return { value: { name: '' } };
  }

  let value: IUpdateType = { name: '', email: '' };
  return {
    value: value,
    onChange(change: Partial<IUpdateType>) {
      value.email = change.email;
    }
  };
};

CreateModel('CREATE');
CreateModel('UPDATE');
CreateModel('foo'); //compile error

Here's an idea of how this might work as a class (also on typescript playground )

type IValueType = { name: string; email?: string };
interface IValue {
  value: IValueType,
  onChange?: (change: Partial<IValueType>) => void;
};

class CreateModel {
  value: IValueType = { name: '' };
  onChange: undefined | ((c: Partial<IValueType>) => void) = undefined;

  constructor(private readonly shouldUpdate: boolean) {
    if (this.shouldUpdate) {
      this.onChange = (change: Partial<IValueType>) {
        this.value.email = change.email;
      }
    }
  }
}

const a = new CreateModel(false);
const b = new CreateModel(true);
Related