Skip to content

Type predicate doesn't narrow down function overload (but assertion works)Β #58660

Closed as not planned
@eachirei

Description

@eachirei

πŸ”Ž Search Terms

type predicate, function overload, discriminating unions

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about method overload, unions

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.4.5#code/C4TwDgpgBAgg+gFSgXigAxgEgN4GdgBOAlgHYDmAvmgNwBQokUAQoiukzvseVXfeNHgkA9iTgAbIsAgEAhuLYByGAGUAIoroNoLEWMnS5C1IqbrNtWgGNR+KNoAKBCABMiV2dKGiJUmfLYACgA3eQAuKFkSEABKCNCFIlxYOD1fQwDkAD4oBIA6fFkCYFwAdSkAC0DlRRi6GxI7R2c3D2ldHwN-YygQ8MjouNyApOZUzr8jFBz8wuKyyuqYWr4AMwBXEitgIlEoVZJm13dPCG99Sfk+8Qio2PiR5PP07qhsKGdgdYISYfEC4BFErlYBVGoxChrTbbXa-WS4XAyYAASVwzy6Rmut0GtwRSOSCSgo3RlwU2FoUCJq16AEIjq1TjBrjEYm8KZT7BUCMIAO5QEgQPkAUQI3IIgQARAA5YTASJQLikMg0iV1dkUWga6y2OX0k5eIIJbH3P5Ep6sbJ-AFAhagpYrbWNXUCJzHNoQJiG-p3IaE0YsJCW2aA+YgsFMB20DZbHZ7A5691Mo0DE1+81Id6fb6-YM2sP2upa6MwvbwxHFVFJ704yJ4+am4mscmUojUwJ0l0tfVnZms5sc0HcvkC4Wi4Ti6Wy+WK8gqtWUjVF6GxuF1lG4JhYlNDMv4hvJANslttjuQV0M9q9o8Drm8-mCqAisWSmVy2QKwhKud0BeayzFlcoCsZxGXHTdZAieAEAAGigMAAAYIhIdYAFsACMZCGZD0JkKNl1hICQK8MDAjQiIA1ghCIhnMgsNQjCCDwmMCOAiBQIITc8i4gBtWQAHkOMo+CAF0Il4yDECEpD6JkYSoAAHygbiyLGGC4MQj9uDIOSoDonCCDZdlW0CBNGUCfiOJZftKQaXBhHECA8nEYQyECABZTwKgKABHYpAgQlkfygDVj0CeNOzdMyLKYKz2Rs2x7Mc5zXI80EfL8gL52Cyx2V3Cs0XMgSYqC2zEqclz3M89LgH8+DApyyksx+KB4LoJdmL2VjTgAJhgEiIJSNIMXkKT+Rkgg9IYpiS1+LrpF6kiVI6C4MnEUaaMm3CAJYoiIAWjjAi4vJeKKoTRKUgaSVW0bsIYuTFOU8jxhW7p1s-cgdM2gzySM1YTIii8zmel5MWi2KOSAhKHPKlKqtwXyasyoKQqpMLDgB7sru6QrLJiazIcaMrksqtL4Yyuqsq1XK10rYHhvEHHivZUroeJ1KvLJxGKb4dkmt+VrNSAA

πŸ’» Code

type A_T = `A${string}`;
type B_T = `B${string}`;

type A_non_literal = 'ASD';
type B_non_literal = 'BSD';

const typePredicateA_non_literal = (val: any): val is A_non_literal => val.startsWith('A');
const typePredicateB_non_literal = (val: any): val is B_non_literal => val.startsWith('A');

function fntypePredicateA_non_literal(val: any): val is A_non_literal { return val.startsWith('A')};

function assertIsA_non_literal(val: any): asserts val is A_non_literal {
  if (!typePredicateA(val)) {
    throw new Error("Not a string!");
  }
}

const typePredicateA = (val: any): val is A_T => val.startsWith('A');
const typePredicateB = (val: any): val is B_T => val.startsWith('B');

function fntypePredicateA(val: any): val is A_T { return val.startsWith('A');}

function assertIsA(val: any): asserts val is A_T {
  if (!typePredicateA(val)) {
    throw new Error("Not a string!");
  }
}

function assertIsB(val: any): asserts val is B_T {
  if (!typePredicateB(val)) {
    throw new Error("Not a string!");
  }
}

function createAorB(a: A_T, p0: number): number
function createAorB(b: B_T, p0: string): number
function createAorB(...[aOrB, p0]: [a: A_T, p0: number] | [b: B_T, p0: string]  ): number {

  if(typePredicateA(aOrB)){
    console.log(Math.sqrt(p0)); // this throws compilation error
  }
  if(fntypePredicateA(aOrB)){
    console.log(Math.sqrt(p0)); // this throws compilation error
  }


  assertIsA(aOrB);
  console.log(Math.sqrt(p0));


  return 0;
}

function create2AorB(a: A_non_literal, p0: number): number
function create2AorB(b: B_non_literal, p0: string): number
function create2AorB(...[aOrB, p0]: [a: A_non_literal, p0: number] | [b: B_non_literal, p0: string]  ): number {

  if(typePredicateA_non_literal(aOrB)){
    console.log(Math.sqrt(p0)); // this throws compilation error
  }
  if(fntypePredicateA_non_literal(aOrB)){
    console.log(Math.sqrt(p0)); // this throws compilation error
  }


  assertIsA_non_literal(aOrB);
  console.log(Math.sqrt(p0));


  return 0;
}

πŸ™ Actual behavior

After the type predicate, the second parameter isn't narrowed down to number, hence the error:

Argument of type 'string | number' is not assignable to parameter of type 'number'.
  Type 'string' is not assignable to type 'number'.

After the assertion, the code works as expected.

πŸ™‚ Expected behavior

Type predicate to do the same narrowing as type assertion.

Additional information about the issue

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions