Búsqueda de sitios web

typescript - Guardia de tipo de propiedad de clase basado en el valor de la propiedad de la instancia


Cómo hacer un ejemplo de clase para inferir el tipo config basado en la verificación del valor de la instancia animalType:

enum Animal {
  BIRD = 'bird',
  DOG = 'dog',
}

type Base = {
    id: number
}

// Object example
type Smth = Base &
  (
    | {
        animalType: Animal.BIRD;
        config: number;
      }
    | {
        animalType: Animal.DOG;
        config: string;
      }
  );

// type guards working
const smthObj: Smth = {
  id: 1,
  animalType: Animal.BIRD,
  config: 1
};

// should be error
const smthObj2: Smth = {
  id: 1,
  animalType: Animal.BIRD,
  config: 'x'
};

if (smthObj.animalType === Animal.BIRD) {
 smthObj.config = 1;
 smthObj.config = 'x'; // should be error
}

// How to make it work the same for class?

class myClass {
  id: number;
  animalType: Animal;
  // this should be based on Animal type
  // number for bird and string for dog
  config: number | string; 

   constructor(id: number, animalType: Animal, config: number | string) {
    this.id = id;
    this.animalType = animalType;
    this.config = config
  }
}

const smthClass: myClass = 1 as any

// I need to make only this check to work
if (smthClass.animalType === Animal.BIRD) {
 smthClass.config = 1;
 smthClass.config = 'x'; // should be error
}

Sandbox de Typescript.

Respuesta #1

Los argumentos de la clase deberían ser parte de una estructura de datos.

Este enfoque es seguro y fácil

enum Animal {
  BIRD = 'bird',
  DOG = 'dog',
}

type Base = {
  id: number
}

// Object example
type Smth = Base &
  (
    | {
      animalType: Animal.BIRD;
      config: number;
    }
    | {
      animalType: Animal.DOG;
      config: string;
    }
  );

// How to make it work the same for class?

class myClass {
  constructor(public props: Smth) {
    this.props = props;
  }
}

const smthClass: myClass = 1 as any

if (smthClass.props.animalType === Animal.BIRD) {
  smthClass.props.config = 1;
  smthClass.props.config = 'x'; // should be error
}

ACTUALIZACIÓN

enum Animal {
  BIRD = 'bird',
  DOG = 'dog',
}


// How to make it work the same for class?

type Either<R> = R extends Animal.DOG ? string : number;

class myClass<T extends Animal> {
  id: number;
  animalType: T;
  config: Either<T>

  constructor(id: number, animalType: T, config: Either<T>) {
    this.id = id;
    this.animalType = animalType;
    this.config = config
  }
}

const smthClass: myClass<Animal.BIRD> = 1 as any

if (smthClass.animalType === Animal.BIRD) {
  smthClass.config = 1;
  smthClass.config = 'x'; // should be error
}

Respuesta #2

Usa genéricos.

enum Animal {
  BIRD = 'bird',
  DOG = 'dog',
}

class myClass<T extends Animal = Animal> {
  id: number;
  animalType: T;
  config: T extends Animal.BIRD ? number : T extends Animal.DOG ? string : never; // this should be based on Animal type

  constructor(id: number, animalType: T, config: T extends Animal.BIRD ? number : T extends Animal.DOG ? string : never) {
    this.id = id;
    this.animalType = animalType;
    this.config = config
  }
}

const birdClass = new myClass(1, Animal.BIRD, 1);

if (birdClass.animalType === Animal.BIRD) {
  birdClass.config = 1;
  birdClass.config = 'x'; // should be error
}

const dogClass = new myClass(1, Animal.DOG, 'x');

if (dogClass.animalType === Animal.DOG) {
  dogClass.config = 1; // should be error
  dogClass.config = 'x';
}

const smthClass: myClass<Animal.BIRD> = 1 as any;

if (smthClass.animalType === Animal.BIRD) {
  smthClass.config = 1;
  smthClass.config = 'x'; // should be error
}

// Type guard function example for any class
function isBirdClass(smth: myClass): smth is myClass<Animal.BIRD> {
  return smth.animalType === Animal.BIRD ? true : false
}

function isDogClass(smth: myClass): smth is myClass<Animal.DOG> {
  return smth.animalType === Animal.DOG ? true : false
}

const unknownClass: myClass = 1 as any;

if (isBirdClass(unknownClass)) {
  unknownClass.config = 1;
  unknownClass.config = 'x'; // should be error
}

if (isDogClass(unknownClass)){
  unknownClass.config = 1; // should be error 
  unknownClass.config = 'x'; 
}

Véalo en TypeScript Playground.

Nota:

  • Usando genéricos, ni siquiera necesitas una protección de tipo porque los genéricos capturarán automáticamente los errores de tipo.