Skip to content

=== is not strong enough to narrow equal values to the same type #63476

@danon

Description

@danon

🔎 Search Terms

equality, narrow, unknown, dynamic type checking

🕗 Version & Regression Information

  • This changed between versions ______ and _______
  • This changed in commit or PR _______
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about _________
  • I was unable to test this on prior versions because _______

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/GYVwdgxgLglg9mABBAhgZygUTCAtgRgB4AVRAUwA8oywATNRDAJxjAHMA+ACgDcUAbEGQBcicAGswcAO5gANIj78YtAGoChaUcQDaAXQCU2xAG8AUIkTA4TRFwgIMigSvWCyiOMGfK1GsmgGphaWiDDevP6IALyxPq7+QeahoUxkUCBMSEpCANwhlgC+IcWWUAAWTDKIYGTSiJhMVUxcBvnFZqCQsAjI6Fg4uABMJORUNPSMUCzs3DkiYmCSMvLxfu5aiLqGxslWNnYOYE5KCe6e3qfrmkkFYRHzMXFXbkK3KZZpGVlrr2T5KVKiCBFSq9Vq9UazVa7TMQA

💻 Code

import {describe, test} from "vitest";
import {assertEquals, assertThrows} from './vitest';

test('Return first value', () => {
  assertEquals('first', castEnum('first', ['first']));
});
test('Return second value', () => {
  assertEquals('second', castEnum('second', ['first', 'second']));
});
test('Throws for empty enum values', () => {
  assertThrows(() => castEnum('value', []), '');
});
describe('Throws for an invalid enum value', () => {
  test('Single valid value', () => {
    assertThrows(() => castEnum('incorrect', ['correct']), '');
  });
  test('Multiple valid values', () => {
    assertThrows(() => castEnum('incorrect', ['correct1', 'correct2']), '');
  });
});

function castEnum<T extends string>(value: unknown, validValues: T[]): T {
  if (validValues.length === 0) {
    throw new Error();
  }
  for (const validValue of validValues) {
    if (value === validValue) {
      return value; // This throws error, but if you return validValue it passes
    }
  }
  throw new Error();
}

🙁 Actual behavior

What I don't understand, if we're already inside if (value === validValue) that means both values value and validValue are the same, so return either value or validValue should pass, and match T.

But actual behaviour is that if you return validValue it works, but returning value, you get an typescript error:

TS2322: Type unknown is not assignable to type T
  T could be instantiated with an arbitrary type which could be unrelated to unknown

🙂 Expected behavior

Returning both values should pass, because we've already checked that they're equal with value === validValue.

Additional information about the issue

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions