Issue
When you define a type like type X = { }
it will have an implicit index signature, so it can pass as a generic parameter that has an index signature whereas the same interface
would not (it's actually the only difference in behaviour I spotted, as I continue...)
type A = {
a: string
}
interface B {
b: string
}
type Constraint<T extends Record<string, unknown>> = true;
type test1 = Constraint<A>
type test2 = Constraint<B> // Error, as expected, since it does not have an index signature
What is the implicit index signature of A though? Why does it behave differently than an explicit index signature?
type A = {
a: string
}
interface C {
[x: string]: string
c: string
}
let a: A = { };
let c: C = { };
a.x = ''; // Error
c.x = ''; // Ok, only an explicitly defined index signature behaves like one
I don't understand what's the point of this implicit signature if it does not behave as one? Was it implemented as a hack to pass generic constraints like in the example above? Does it have an actual effect on type checking beyond that case?
Solution
Implicit index signatures as implemented in microsoft/TypeScript#7029 were introduced to fix microsoft/TypeScript#6041 and enable object literals to be assigned to index signatures. That is, the issue being addressed is one of assignability.
Before TypeScript 2.0, if you tried to assign an object literal to a variable and then tried to assign that variable to a type with an index signature, it would always fail:
function yell(dict: { [k: string]: string }) {
for (const k in dict) {
console.log(k + ": " + dict[k].toUpperCase() + "!!!")
}
}
let x = { a: "hello", b: "goodbye" };
// ^? let x: {a: string, b: string}
// TS 1.8 and earlier:
yell(x); // error!
// ~ <-- {a: string, b: string} has no index signature
This was annoying and confusing, as described at Why can't I indirectly return an object literal to satisfy an index signature return type in TypeScript?. And so we now have implicit index signatures, so that such assignments are allowed:
// TS 2.0 and later:
yell(x); // okay
Note that it is not intended that you can actually index into object literal types with any key. It is just for assignability.
Do note that there was a plausible reason for the original error. Object types in TypeScript are not sealed or exact (see microsoft/TypeScript#12936), meaning that a value of type {a: string, b: string}
could have extra properties unknown to TypeScript and these could fail to match the index signature:
let y = { a: "hello", b: "goodbye", c: 12345 };
x = y; // allowed!
yell(x); // allowed due to implicit index signature, but runtime error!
So implicit index signatures are technically unsafe. But they are just too convenient to give up, and the alternative is too inconvenient.
Answered By - jcalz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.