Skip to content

exactOptionalPropertyTypes: strict type equality is too lenientΒ #61547

Closed
@rauschma

Description

@rauschma

πŸ”Ž Search Terms

"exactOptionalPropertyTypes", "strict type equality"

πŸ•— Version & Regression Information

  • Tested in TypeScript 5.8.2

⏯ Playground Link

https://www.typescriptlang.org/play/?exactOptionalPropertyTypes=true#code/PTAEFMA8EMGMBcDyAHeBLA9gO2gGwAoBOGy4h8AngCoWkDOAXAFBOWmhUCMoAvKAN5NQoZMWQB+BqDrxCaLAHMANEwC+rWuA4AmXgKEixk0AFcsAE3AAzeeHOgAPtNnzlajewD6egNoGQoIAy5Bzc0BY6oNCEWrDYdGiW0fYycgi4FBAAjiZ4BgCCdHRk8AA8AKLZeCUGwlxKOgYAfI0qwgFckeFUulFaWBjwoLFY8Yl2oLgYGEXpWTm4+YXFJQByAyUAMlNFFfPVwrWc9d1Nza2g7bpodKD9g9CFaAo4AEa4WvAYIYtF5KvrBXiz2gb3A+wOdQawmaLX8YA610ij2BoNAnyhoEBy0BT1e73BtW0x04pxUAF0ANwsNhaLF-TxUCCQeDgCw3WQmcCNPQANwwCSpAUAvBuAI-2OKkANZor4uBQKMjOVKDGlzPBoSgAOlAAGUMCZCLBwFIABbweDIRggBTq40mF4a2IAW2AAFk0LBiHQMFZ4MAaKRtR60KhgNc6Jy6MBtAB2AAM2gALABiMOcp2O1nwAC0Ce0nAArNoAJwANnzsY8Wl2VQAGvUAJrcngGAAUJSojRbAEpeNzGVAWWzQDXQOJQNwpNoewPWeY6K3252ezw+0zB3PQPXR+PQJOe2OOVopFY8EUDFSVVtpuBq7gSnXN02DD4a2S17Obj562SDGOW1+3xnIcXzfA9CE5XdQBPXAii7Axj1PcBz0rUA1lKAAhd8hxeKZ3jCJ82jAAADW8iNAF5wFgaATCKUAAHcwmVL5oD5BJUPWMIKG5CjXFAIjoKKMiMEIPjDyIgxbxKdD6kPbkgI3Q8am3ASkIOSCW0k6SoMQuTmQ-NFwK0MCIKkLBwB5Mg4OEC9NExZE8TBKgonleB6l1fVDSbUAfHcg1wEAvTgKcwgXNAgyTO0mDwCpIA

πŸ’» Code

// exactOptionalPropertyTypes:

type T1 = {
  prop?: string,
}
type T2 = {
  prop?: undefined | string,
}
type _ = [
  // ❌ T1 and T2 are considered strictly equal
  Assert<Equal<
    T1, T2
  >>,
  // T2 is not assignable to T1
  Assert<Not<Assignable<
    T1, T2
  >>>,
  // T1 is assignable to T2
  Assert<Assignable<
    T2, T1
  >>,
  // T1 and T2 are not considered loosely equal
  Assert<Not<LooseEqual<
    T1, T2
  >>>,
];

type Assert<_T extends true> = void;
// 🟒 Trick to trigger strict type equality. Source: https://github.com/Microsoft/TypeScript/issues/27024#issuecomment-421529650
type Equal<X, Y> =
  (<T>() => T extends X ? 1 : 2) extends
  (<T>() => T extends Y ? 1 : 2) ? true : false
  ;
type LooseEqual<X, Y> =
  [X] extends [Y]
  ? ([Y] extends [X] ? true : false)
  : false
  ;
type Not<B extends boolean> =
  // `Equal` because want to avoid Not<any> being `false` or `true`
  Equal<B, true> extends true
    ? false
    : (Equal<B, false> extends true ? true : never)
  ;
type Assignable<Target, Source> = [Source] extends [Target] ? true : false;

πŸ™ Actual behavior

T1 and T2 are considered to be strictly equal – even though T2 is not assignable to T1.

πŸ™‚ Expected behavior

T1 and T2 should not be considered strictly equal.

Additional information about the issue

Metadata

Metadata

Assignees

No one assigned

    Labels

    Not a DefectThis behavior is one of several equally-correct options

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions