-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Satisfies does not work with const assertion #51173
Comments
Try reversing the order of
This is immaterial as |
Yes, it works when reversed as I show in the playground link, but that just creates an unnecessary restriction to make sure everything in the The compiler may be treating it as an expression, but it's not, and therein lies the bug. |
It absolutely is an expression, though. Regardless, |
I donβt think thatβs the ask here, to be fair. It seems like the intent is to do the |
Giving it some more thought, in the OP we can just reverse the order of operations, and it works, but arguably that actually doesn't generalize: const j: { m: readonly [1, 2] } = { m: [1, 2] } satisfies { m: number[] } as const; I'm not sure how to resolve this situation coherently. The implied algorithm seems to be:
But in TypeScript, expressions only have one type. If we just "threaded through" the
Another way to put it is that The same thing is broadly true of contextual types in general, which again aren't a type-to-type transform, but rather have effects on the interpretation of literals and other expressions. In the original |
Yep, hence this issue π
Indeed this seems to be what OP wants to do. |
As I understood from your writing in #47920,
Well, that depends on
In other words, if the And stating that there's only one order that works is not correct either. The following works just fine: const a = [x satisfies T1, y satisfies T2] as const; I need much more time to digest your latter comments as I am not an expert on the compiler code, but thanks for starting to pull a 180. π |
That works because the const assertion applies to the array literal What Ryan is saying is that the behavior you want isn't possible with the current way const assertions work because |
That's the part I'm stuck on. Why would it ever apply to the satisfies expressions if they are purely checks?
Isn't that exactly what it does in the case of In any case, you guys clearly know the compiler way better than me, but I think my expectations of what it "should" do are fairly logical. Thanks for taking the time to explain. |
It wouldnβtβwhich is the problem here. (in simpler terms |
It probably would have made more sense for const assertions to instead have been written as |
Alright, I think I get what you're saying now. Basically the architecture didn't fully envision more than one post-expression operator, or at least not one like Thinking perhaps very naively, couldn't you just trick the compiler internally by doing something like: { ... } as const = ([{ ... }] as const)[0] Makes the error go away in the playground at least. |
Itβs not really an operator, thatβs the thing. Itβs a modifier. Itβs hard to explain but basically |
@steverep The confusion here is essentially linguistic: think βprairie dogβ vs βprairie manβ. Syntactically, the const assertion looks like the former to the compiler. |
@tjx666 please submit text, not screenshots. No one wants to spend multiple minutes typing in examples just to try some things out. |
It's also not accessible to anyone using a screen reader or with low vision |
trackEvents.ts type EventName = typeof events[number]['name'];
type TrackEvent = {
name: string;
id: number;
};
type FilterElementByName<
Arr extends readonly TrackEvent[],
N extends EventName,
> = Arr extends readonly [infer First, ...infer Rest]
? First extends TrackEvent
? First['name'] extends N
? First
: Rest extends readonly TrackEvent[]
? FilterElementByName<Rest, N>
: null
: null
: null;
export type TrackEvents = {
[K in EventName]: FilterElementByName<typeof events, K>;
};
const events = [
{
name: 'event1',
id: 10001,
click_type: 'xxx',
client_name: 'PS',
},
{
name: 'event2',
id: 10002,
work_id: '',
},
{
name: 'event3',
id: 10003,
work_id: '',
export_status: 2 as 0 | 1 | 2,
},
// I want the element get the intellisence of TrackEvent
] satisfies TrackEvent[] as const;
export const trackEvents = events.reduce((obj, event) => {
obj[event.name] = event as any;
return obj;
}, {} as TrackEvents); The type type TrackEvents = {
event1: {
readonly name: "event1";
readonly id: 10001;
readonly click_type: "xxx";
readonly client_name: "PS";
};
event2: {
readonly name: "event2";
readonly id: 10002;
readonly work_id: "";
};
event3: {
readonly name: 'event3',
readonly id: 10003,
readonly work_id: '',
readonly export_status: 2 as 0 | 1 | 2,
}
} |
As a workaround the Immutable type of @lauf/store aligns with the recursive readonly behaviour of I typically end up with Immutable in my codebases for reasons like this, even where I'm not using the store itself - it's a tiny package anyway, and getting smaller in v2.0.0. Regardless it won't be bundled if imported as type-only. Here's an example where I can derive import type { Immutable } from "@lauf/store"
export const companies = [
{
id:"Someone",
url: `https://someone.io`,
tags: [
"analytics",
"datascience"
],
description: <>
Quality management for data science product lifecycles
</>
}
] as const satisfies Immutable<Company[]>;
interface Company {
id: string;
url: `https${string}`
tags?: string[],
description?: JSX.Element;
}
type CompanyId = typeof companies[number]["id"]; I've found this to be useful enough to need it in all my projects anyway, so maybe a Const is a candidate for a core Typescript utility type so that people can easily align with |
Just sharing an alternative based on type-fest ReadonlyDeep. That should work with any type (map, arrays, nesting...). import type { ReadonlyDeep } from 'type-fest';
interface BaseConfig {
url: string,
navLinks: Array<{ title: string, href: string}>
}
export const siteConfig = {
url: 'https://',
navLinks: [ { title: 'Blog', href: '/blog' } ]
} as const satisfies ReadonlyDeep<BaseConfig>;
// eventually
export type SiteConfig = typeof siteConfig;
// siteConfig.url => 'https://' |
#55229 addressed this. I think that this addressed most of the concerns here - you still have to |
I agree; the adversarial example I made const j: { m: readonly [1, 2] } = { m: [1, 2] } as const satisfies { m: number[] } ; which errored at time of writing now works. I don't see anything left to do here. |
Bug Report
π Search Terms
satisfies, as const, literal
π Version & Regression Information
satisfies
is new in 4.9.0 betaβ― Playground Link
Playground Link
π» Code
π Actual behavior
Fails with error:
π Expected behavior
No error because it is a literal and
satisfies
should not change the type. Also, other type assertions work fine, soconst
should as well.The text was updated successfully, but these errors were encountered: