4

In the below type definitions why do P1 and P2 have different values?

type P1 = (() => 22) extends {[k:string]:any} ? 1:2 //`P1 == 1`
type P2 = (() => 22) extends {[k:string]:unknown} ? 1:2 //`P2 == 2`
Giorgi Moniava
  • 27,046
  • 9
  • 53
  • 90

1 Answers1

1

See Breaking Change: { [k: string]: unknown } is no longer a wildcard assignment target for an authoritative answer.

The index signature {[k: string]: any} behaves specially in Typescript: it's a valid assignment target for any object type. Usually types like () => 22 are not given implicit index signatures, and so it is an error to assign () => 22 to {[k: string]: unknown}. Only when the property type is any do you get the special permissive behavior:

let anyRec: { [k: string]: any };
anyRec = () => 22; // okay

let unkRec: { [k: string]: unknown };
unkRec = () => 22; // error

So that's why P1 and P2 differ.

Playground link to code

jcalz
  • 264,269
  • 27
  • 359
  • 360
  • in your [link](https://github.com/microsoft/TypeScript/wiki/Breaking-Changes/83af27fca396d172b4d895d480b10c3bacf89112#-k-string-unknown--is-no-longer-a-wildcard-assignment-target) it says: "arg would have had the type { [k: string]: unknown }, which anything is assignable to, so the call would have *incorrectly* been allowed". Why was the call *incorrect* in that scenario? – Giorgi Moniava Dec 03 '22 at 11:05
  • The relevant change was implemented in [ms/TS#30637](//github.com/microsoft/TypeScript/pull/30637), which made unconstrained generic type parameters default to `unknown` instead of `{}`. It was [this commit](//github.com/microsoft/TypeScript/pull/30637/commits/5dce9909) that says continuing to allow `{[k: string]: unknown}` to be a universal assignment target for all objects would have caused previously caught errors to go uncaught. I don't know if that makes it "incorrect", but it's the closest I can get to an answer to your follow-up question. – jcalz Dec 03 '22 at 16:00