hirohe
hirohe

Reputation: 21

TypeScript definition, how to constraint differently for a param of function, by matching certain value in param

I have a function need to define, such a function's usage can be simplify as below:

test({ flag: 1, payload: 123 }) // is ok
test({ flag: 1 }) // not ok
test({ flag: 2, payload: 123 }) // is ok
test({ flag: 2 }) // is ok

I was thinking of using function overloading to write such definiton. but turns out all definitions of function overloading is 'Or' relationship, and i can not define a type that exclude 1 from number

interface TestFunc {
  (data: { flag: 1, payload: number }): void
  // can I exclude 1 from number ?
  (data: { flag: number, payload?: number }): void
}

const test: TestFunc = (data) => {
  // ...
}

test({ flag: 1 }) // it should report type error

Upvotes: 0

Views: 78

Answers (2)

You can achieve smth similar to negation:


type NotOne<T extends number> = T extends 1 ? never : T;

type WithOne = {
  flag: 1,
  payload: number
}

type WithoutOne<T extends number> = {
  flag: NotOne<T>,
  payload?: number
}

type Data<T extends number> = WithOne | WithoutOne<T>

interface TestFunc {
  (data: WithOne): void
  <T extends number>(data: WithoutOne<T>): void
}

const test: TestFunc = <T extends number>(data: Data<T>) => {
}


test({ flag: 1, payload: 123 }) // is ok
test({ flag: 1 }) // not ok
test({ flag: 2, payload: 123 }) // is ok
test({ flag: 2 }) // is ok

Playground

I have created NotOne utility type for negation purpose. As you see, nothing complicated, it just returns never if value is 1.

Then, you need to make illegal state unrepresentable with union help Data<T>

Then, you need just use this union of two valid states to overloaded function.

Very similar example you can find in my blog

Upvotes: 0

falinsky
falinsky

Reputation: 7428

You can use extends to detect if your flag property equals to 1 - and correspondingly build the part with payload:

interface Payload {
    payload: number;
}

function test<T extends number>(data: { flag: T } & (T extends 1 ? Payload : Partial<Payload>)): void {

}

test({ flag: 1, payload: 123 }) // is ok
test({ flag: 1 }) // not ok
test({ flag: 2, payload: 123 }) // is ok
test({ flag: 2 }) // is ok

You can play with this example at the playground.

Upvotes: 2

Related Questions