Repository navigation
Description
Bug Report
🔎 Search Terms
const Type Parameters, mapped types, self types
🕗 Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about
constType Parameters (but there's nothing there about them yet)
⏯ Playground Link
Playground link with relevant code
💻 Code
type WithSchema<Self> = {
// This ensures Self is inferred to be the entire object this type is applied
// to, while the resulting type leaves the *values* of those keys as
// `unknown`.
[K in keyof Self]?: K extends never ? Self[K] : unknown;
} & (("schema" extends keyof Self ? Self["schema"] : {}) extends infer Schema
? {
[K in keyof Schema]?: Schema[K] extends "string"
? string
: Schema[K] extends "number"
? number
: never;
}
: never);
declare function doSomething<const Self>(o: WithSchema<Self>): void;
// doSomething's Self isn't automatically `as const`, despite declaring `const Self`.
doSomething({
schema: {
name: "string",
age: "number",
},
name: "Alice",
age: 35,
});
// Explicitly using `as const` works.
doSomething({
schema: {
name: "string",
age: "number",
},
name: "Alice",
age: 35,
} as const);🙁 Actual behavior
doSomething's Self parameter was not inferred as const.
🙂 Expected behavior
doSomething's Self parameter should be inferred as const, since it's marked as const and inferred from a literal at the call site.
More context
I'm actually working on something more complex than this: typing JSON-LD documents. The core of the issue, though, is that the type of some keys of the object depends on another key (here, schema). I'm doing this using a type parameter which infers as the type of the given object. This uses the same technique described in #52088 (which proposes a more native version using a new keyword).
Currently, the only way to make these types useful is to ask the user of these types to use as const everywhere. Of course, this is less than ideal, which is exactly why const Type Parameters were introduced. But it appears the more complex usage I have breaks that feature, reverting to inferring a widened type.
Activity
RyanCavanaugh commentedon Jun 5, 2023
Looking at the definition, this seems like arguably the intended behavior.
WithSchemaintentionally doesn't present any top-level definition of self, so there isn't any hint that says that a usage ofWithSchema<Self>should get the constness of the type parameter.Without reading the commentary here, I might think that someone did all this work in
WithSchemajust to avoid having theconstness propagated into the argument expression.whzx5byb commentedon Jun 6, 2023
Petra Jaros (@Peeja)
I have a workaround for you.
I'm not quite sure but it seems that if you have a naked
Selfin either branch of a conditional type ([Self] extends [unknown] ? WithSchema<Self> : Self), the compiler will treatSelfasconsteven it is never used.Peeja commentedon Jun 6, 2023
Ryan Cavanaugh (@RyanCavanaugh) Yeah, that does make sense.
whzx5byb Ooh, that's perfect! I've reworked it a bit to make it more readable and more reusable. Here's
InferringSelf!This also takes over all of the
Selfmagic, so the mapped type at the beginning ofWithSchemacan be the much more reasonable and meaningful{ [K in keyof Self]?: unknown }.I'm happy considering this a real solution and not just a workaround. Ryan Cavanaugh (@RyanCavanaugh), I didn't see anything in your comment that sounded like you saw what you'd consider a bug here, so I'll close. If you did see something worth addressing, obviously feel free to re-open.
Thanks, both! ❤️🎉
Remove need for `as const` (🎉)
replace transactWrite add* methods with single push method allowing c…