Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional type based on a non-empty string

Tags:

typescript

There are TONS of questions about conditional types based on a value. However I'm not sure can scenario below be implemented at all.

I'm getting acquainted with conditional types and I'm wondering if it's possible to make a conditional type that will be dependant on a non-empty string.

Let's consider next interface:

interface Animal {
    dogName: string;
    canBark: boolean;
}

and

interface AnimalToBeMapped {
    dogsNickname?: string;
    /// some other props
}

I'm using this interface to map an object

function mapAnimal(animal: AnimalToBeMapped): Animal {
    return {
        dogName: animal.dogsNickname,
        canBark: !animal.dogsNickname
    }
}

I'm setting canBark property to true only when the argument object has a dogName property set (should work also for empty string vs. non-empty string).

What I want to do is to add additional type-safety to my Animal interface (if someone is setting dogName manually to be defined then I want to force canBark property to be true and vise versa).

example:

const exmpl1: Animal = {
    dogName: 'Rex',
    canBark: false // I want an error here
}

and

const exmpl2: Animal = {
    dogName: '',
    canBark: true // An error here
}
like image 956
anotheruser Avatar asked Sep 04 '25 01:09

anotheruser


1 Answers

Please let me know if it works for you


type NonEmptyString<T extends string> = T extends '' ? never : T;

type WithName = {
  dogName: string,
  canBark: true,
}

type WithoutName = {
  dogName?: '',
  canBark: false
};

type Animal = WithName | WithoutName;


type Overloadings =
  & ((arg: { canBark: false }) => Animal)
  & ((arg: { dogName: '', canBark: false }) => Animal)
  & (<S extends string>(arg: { dogName: NonEmptyString<S>, canBark: true }) => Animal)

const animal: Overloadings = (arg: Animal) => {
  return arg

}

const x = animal({ dogName: '', canBark: false }) // ok
const xx = animal({ dogName: 'a', canBark: true }) // ok
const xxx = animal({ dogName: 'a', canBark: false }) // error
const xxxx = animal({ dogName: '', canBark: true }) // error
const xxxxx = animal({ canBark: true }) // error
const xxxxxx = animal({ canBark: false }) // ok

Playground

It is not so easy for TS to find distinguish between empty string and string. Type string is assignable to literal type '' (empty string), that's whu I used NonEmptyString helper

UPDATE If you are interested in a bit simplier solution, which is not requires extra funxtion, see this example:


type WithName = {
  dogName: string,
  canBark: true,
}

type WithoutName = {
  canBark: false
};

type Animal = WithName | WithoutName;

const x: Animal = {
  dogName: 'name',
  canBark: true
}; // ok

const xx:Animal={
  canBark:false
}; // ok

const xxx:Animal={
  canBark:true
} // expected error

const xxxx:Animal={
  dogName:''
} // expected error

Playground

like image 126
captain-yossarian Avatar answered Sep 07 '25 19:09

captain-yossarian