Skip to content
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

Typing error when adding an element to a generic Map not catched by Typescript #60045

Closed
ns-fjolliton opened this issue Sep 24, 2024 · 6 comments
Labels
Not a Defect This behavior is one of several equally-correct options

Comments

@ns-fjolliton
Copy link

πŸ”Ž Search Terms

map generic subtyping (Not sure about the correct terms for my issue)

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ

⏯ Playground Link

https://www.typescriptlang.org/play/?ts=5.7.0-dev.20240924#code/MYewdgzgLgBA5gUygeQE4GFUIIZQTAXhgB4BpAGhgDUA+ACgFsAuGAWWwAczLbKBrBAE8WFGA2wCWdAJSEa1aSypyYAbwBQMLTAA2SGAjBRUgwmIB0iKHQGDpAbk3aAlgDMYdQ8dMFfMAK5gACYIrs5gCEGyGtqxBkYmZuICMo5x2gzmEEg2QpReJg5OWgC+xTBYUP6oYPHejiWO6qCQsHjQLOxc0KjhcJSq2Cxg-gwARgiolGPDoxOoJfJEEQDubJyOVmiYOHh07VCUAOSuICBHlDIqdIMsAIwl0kUA9M8wAHLIMACiAEq-yF+lAAQgBVAAqMAA6gD3gBxGDggCaAAVvgBCTHNcAQEB6cw6EBwfYIaCWHInM5HJ72GCvFSqABE2EZ9xKQA

πŸ’» Code

const getOrCreate = <K, V>(m: Map<K, V>, key: K, make: () => V): V => {
    let entry = m.get(key);
    if (entry === undefined) {
        entry = make();
        m.set(key, entry);
    }
    return entry;
};

const test: Map<string, {a: number, b: number}> = new Map;
getOrCreate(test, 'foo', () => ({a: 1}));
// NO ERROR, BUT WRONG TYPE!!!
console.log(test.get('foo')); // => {"a": 1}

πŸ™ Actual behavior

I was able to add a value to a map that was in contradiction with its typing.

πŸ™‚ Expected behavior

A compilation error somewhere. Maybe the code example is not great, but in any case, something should be reported.

Additional information about the issue

Alas, I keep finding issues like this. I'm less and less confident in Typescript. I'm working on a large code base, and I'm worrying that there might be other issues like this undetected.

About Typescript, this might be for historical reasons, or because it's hard to retrofit typing to a dynamic language, but there should some options to warn about all sort of possibilities for typing holes. It's important to be able to feel confident about the typing.

@jcalz
Copy link
Contributor

jcalz commented Sep 24, 2024

Covariant properties + aliasing + mutation is a known source of unsoundness:

const x = { a: "abc" };
const y: { a: unknown } = x; 
y.a = 123;
x.a.toUpperCase(); // πŸ’₯

This just is how it is, see #9825 and #55451 and #55763.

@ns-fjolliton
Copy link
Author

Thanks for the pointers.

Couldn't Typescript add an option to be warned about these cases? I'm sure it would report lot of things, mostly false-positive cases, but at least this could be reviewed.

@MartinJohns
Copy link
Contributor

Alas, I keep finding issues like this. I'm less and less confident in Typescript.

You should be aware that having a sound type-system is explicitly not a goal of TypeScript, but instead having a balance between correctness and productivity. There will always be unsound type holes in the language. The language has gotten much much better over the years tho.

If you need a perfectly safe type system you might want to look into other languages that can transpile to JavaScript.

TypeScript Design Goals

there should some options to warn about all sort of possibilities for typing holes

That's fairly unrealistic. If they can be safely identified, then they're likely either going to be fixed, or intentional for some use cases and errors wouldn't make sense. Also note that TypeScript has no concept of warnings, only errors. The best option you have is to use a longer and enable all sorts of rules.

@RyanCavanaugh
Copy link
Member

If you're doing mutation, you need to be much more careful when writing generic signatures. In this case I'd write

const getOrCreate = <K, V>(m: Map<K, V>, key: K, make: () => NoInfer<V>): V => {

which generates an error as expected at the incorrect call site

@RyanCavanaugh RyanCavanaugh added the Not a Defect This behavior is one of several equally-correct options label Sep 24, 2024
@fjolliton
Copy link

I was not aware of NoInfer. It better matches the safety and convenience I was looking for. Thanks. It doesn't help my constantly degrading feeling about Typescript, but that's interesting to know.

We can close this issue since it is obviously not going to change anytime soon (or ever).

Anyway, maybe it's time I give Melange a try :)

@RyanCavanaugh
Copy link
Member

Whoever told you TS was Haskell for JS clearly led you down the wrong path πŸ˜‰

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Not a Defect This behavior is one of several equally-correct options
Projects
None yet
Development

No branches or pull requests

5 participants