Reputation: 36826
How to make class example to infer config
type based on animalType
instance value check:
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
}
Upvotes: 2
Views: 841
Reputation: 7080
Use Generics.
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';
}
See it in TypeScript Playground.
Note:
Upvotes: 1
Reputation: 33041
Class arguments should be a part of one data structure.
This approach is safe and easy
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
}
UPDATE
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
}
Upvotes: 3