-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
5.1.3 new "feature"? Branded types are preserved in const
template literal
#54648
Comments
I think this was intentional? (I at least explicitly added it and tests for it, though in the course of fixing other stuff.) Also @weswigham |
Though, I'm not sure how this should interact with as const... I guess it's a noop and that's normal? |
The relevant PR is #52836, note that the tests contain effectively exactly what the original post contains, a branded type inside a template. |
Ah, template literals, that's the word I was looking for (instead of string interpolation). Had a brain fart, my apologies. |
const
string interpolationconst
template literal
This issue has been marked as "Working as Intended" and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
I'm reopening this one. The original issue, #48034, was that we're not "untagging" literal types when they occur in template literal placeholders. In other words, the expectation was that type S1 = `xyz${"a"}`;
type S2 = `xyz${"a" & { tag: any }}`; both resolve to |
I think so. But we still want to revert #48044 also. I think all we need is support for "popping tags" on literal types (such that I really don't want to mess with supporting generalized intersections in placeholders. It doesn't add much in terms of expressiveness, but it adds a lot in terms of complexity and possible performance issues related to normalization. |
There's also #54188 which I really don't like. We decided to allow |
I guess we'll talk about it in the design meeting, but I can't help but feel a little sad given this issue was about the construct being useful, and I found the branded case to be a valuable idea: https://github.com/microsoft/TypeScript/blob/e170bc59d4ba0d335ce86f66296bec71c5018317/tests/cases/compiler/templateLiteralIntersection2.ts I'll have to resurrect the perf test I had that motivated my follow-up. |
@jakebailey I saw that test, but I'm not sure I've seen an actual user request for the feature. Other than just stripping tags. I'm just not convinced anything beyond that is worth the complexity. |
Just to link it (since it's in the middle of a thread), #52345 (comment) is the perf case I had been looking into. |
My inclination was otherwise, but since the consensus in the design meeting trended towards preserving tagged literals in placeholders, I'm going to go along. We still need a few fixes however, such as #53427 and not descending into template literals in |
Funny, I had the opposite takeaway from the meeting given the potential perf benefit π |
My chief performance concern was the overhead of descending into template literal types in |
This behavior is causing a few types in In term of soundness, IMO it does make sense that in JS, it would be: const extendedStr = Object.assign('abc', { a: 1 })
console.log(`${extendedStr}`) // 'abc' the reasoning being the |
@unional That is a valid point, indeed. |
It didnβt come up in the original discussion, but I might as well add it now. The specific use case for this relates to DynamoDB keys. A common pattern for such keys is, for example, i.e., this: type UserID = string & { [BRAND]: "UserID" };
type Key = `USER#${UserID}`; vs. this: type KeyWide = `USER#${string}`; Of course, there are workarounds, such as a helper function to enforce a const formatUserKey = (userID: UserID): KeyWide => `USER#${userID}`; but it's convenient to be able to write them literally as well, and have stronger assurances: const foo = "foo";
const key: Key = `USER#${foo}`; // oops! So I would like to see this stick around (as I mentioned, it has proven valuable), however, I do understand that "it's convenient" is not the strongest of arguments, especially if this is causing issues elsewhere. |
Bug Report
π Search Terms
branded, interpolation, 5.1.3
π Version & Regression Information
This changed between versions 5.0.4 and 5.1.3
β― Playground Link
Playground link with relevant code
π» Code
π Actual behavior
The intersected brand is preserved during string interpolation for variables and types!
π Expected behavior
Pre 5.1.3, the intersected brand always got removed during string interpolation for variables and types.
This is part bug report and question. My question being in future versions, will this behavior be preserved? I wrote a clunky generic function and type to do this exact thing because I found it valuable, but I'd much rather rely on native behavior if this is going to stick around π
The text was updated successfully, but these errors were encountered: