The Wayback Machine - https://web.archive.org/web/20210122000032/https://github.com/microsoft/TypeScript/issues/18433
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

Dangerous "name" (and potentially others) global #18433

Open
pocesar opened this issue Sep 13, 2017 · 69 comments
Open

Dangerous "name" (and potentially others) global #18433

pocesar opened this issue Sep 13, 2017 · 69 comments

Comments

@pocesar
Copy link

@pocesar pocesar commented Sep 13, 2017

TypeScript Version: 2.6.0-dev.20170913

Code

if (name === "asdf") {
  console.log("asdf")
}

https://www.typescriptlang.org/play/#src=if (name %3D%3D%3D "asdf") {%0D%0A console.log('hello')%0D%0A}

Expected behavior:

Error out

Actual behavior:

More than once, I had a "local" variable called name, outside a block scope, and it didn't warn me that it's not defined (or used before declaration). even though, in my code, event.name is a string, the global name variable is of type never. it's defined in lib.dom.d.ts, along with other "juicy" global names that will provide some confusion, like length, external, event, closed

image

image

one 'fix' (that would be a breaking change) would to make lib.dom-globals.d.ts (the propertiers that are usually accessible through window variable) and make it an opt-in in tsconfig.json compilerOptions.lib array

@kitsonk
Copy link
Contributor

@kitsonk kitsonk commented Sep 13, 2017

Duplicate of #9850

@pocesar
Copy link
Author

@pocesar pocesar commented Sep 13, 2017

that didn't fix the problem. you can still use the variable and the nightly will happily use it without complaining, so not really a duplicate, but a regression

the reason for an opt-in is because some typings need "DOM" declarations (like pouchdb), and even if you're using node, you need to use "lib.dom", just because you need the Blob declaration.

@RyanCavanaugh
Copy link
Member

@RyanCavanaugh RyanCavanaugh commented Sep 13, 2017

We keep going around on this and need to come up with something that really solves the issue

@DanielRosenwasser
Copy link
Member

@DanielRosenwasser DanielRosenwasser commented Sep 14, 2017

Spoke with @mhegazy - let's change it to void so it's not comparable to anything?

@pocesar
Copy link
Author

@pocesar pocesar commented Sep 14, 2017

how about a tiny breaking change for 2.6 and separate the global DOM declarations into another lib file? is it feasible?

@mhegazy
Copy link
Contributor

@mhegazy mhegazy commented Sep 14, 2017

how about a tiny breaking change for 2.6 and separate the global DOM declarations into another lib file? is it feasible?

u can do this today with --lib es6 that does not include the dom.

@pocesar
Copy link
Author

@pocesar pocesar commented Sep 15, 2017

but I have typings that rely on DOM stuff, even if it's not actually used in node.js (like pouchdb), but there are many other isomorphic typings

@kitsonk
Copy link
Contributor

@kitsonk kitsonk commented Sep 17, 2017

Well then you include the DOM library... It doesn't make any sense to have a partial DOM library, or even a globals DOM library... Because I am assuming you aren't suggesting that the global window and document get moved to this separate global DOM declarations lib file. Why only half model something? That sounds very dangerous.

@pocesar
Copy link
Author

@pocesar pocesar commented Sep 17, 2017

properties of "window" that are considered globals is there, under window global. you shouldn't be using globals anyway. how is that dangerous? you should be using window.name and not name anyway. or window.menubar instead of menubar. classes(ish) are properly named, like Blob, Uint8Array, JSON, and you know you won't be crossing with those by mistake. if you DO want to use browser globals like it's 1999, it's up to you to include the legacy way of accessing global variables that are actually under window

@kitsonk
Copy link
Contributor

@kitsonk kitsonk commented Sep 17, 2017

if you DO want to use browser globals like it's 1999, it's up to you to include the legacy way of accessing global variables that are actually under window

Not using them doesn't make them go away... So you are suggesting that the libraries not reflect the actual environment?

@pocesar
Copy link
Author

@pocesar pocesar commented Sep 17, 2017

not sure if trolling or serious.

@kitsonk
Copy link
Contributor

@kitsonk kitsonk commented Sep 17, 2017

You implied breaking out the DOM definitions. Mohammed told you how you can do that today. You then implied that wasn't good enough and seemed to imply something else, like that the global you don't like be located in a separate library file. Why do that, or more specifically what are you suggesting, because it appears to make no sense to me.

@RyanCavanaugh
Copy link
Member

@RyanCavanaugh RyanCavanaugh commented Oct 10, 2017

void is a slightly safer type than never, so we'll change this. Should also modify length since it's also problematic

@falsandtru
Copy link
Contributor

@falsandtru falsandtru commented Oct 11, 2017

Can you remember #15424? And I guess string | void is better than void.

@RyanCavanaugh
Copy link
Member

@RyanCavanaugh RyanCavanaugh commented Oct 11, 2017

string | void will still allow string writes

@mihailik
Copy link
Contributor

@mihailik mihailik commented Oct 11, 2017

Should it be never | void?

Aside from anything, it sounds REALLY cool — might sell some T-shirts and other memorabilia.

@falsandtru
Copy link
Contributor

@falsandtru falsandtru commented Oct 11, 2017

Certainly string | void is not perfect for type safety of local variables but it has a good balance between type safety and usability of global name variable. So I think it is also reasonable.

@mihailik
Copy link
Contributor

@mihailik mihailik commented Oct 11, 2017

@falsandtru the global name can be accessed/assigned via typecast with little to no fuss. TS even elides any extra brackets.

Signalling up both name and length would be really helpful in real life code: people do make those errors. Creative declaration for name is a cheap way to get there, alternatively the compiler may have a special handling just for these specific globals.

@falsandtru
Copy link
Contributor

@falsandtru falsandtru commented Oct 11, 2017

name: void is also acceptable for me.

@falsandtru
Copy link
Contributor

@falsandtru falsandtru commented Oct 12, 2017

A patch exists here: microsoft/TypeScript-DOM-lib-generator#240

@mihailik
Copy link
Contributor

@mihailik mihailik commented Oct 12, 2017

Simple suggestion: remove name and length declarations altogether. That will error on any use of name/length in global scope, and with a very obvious error message.

Playground link

function use(obj) {
    ñame = obj.name;
    ~~~~
    Cannot find name 'ñame'.
}

Declare name and length in your code if you really need it. Whoever wants to shoot themselves in the foot — they bring their own gun.

@mihailik
Copy link
Contributor

@mihailik mihailik commented Oct 12, 2017

Besides, look at this misleading error you get in current TS:

myname = 'Allen'
~~~~~~
Cannot find name 'myname'. Did you mean 'name'?

The existing declaration trickery pushes people into using name global. Not ideal.

@RyanCavanaugh
Copy link
Member

@RyanCavanaugh RyanCavanaugh commented Oct 12, 2017

@mihailik insufficient because you can still do this

var name;
if (someCond) { name = "hello"; }
if (name !== undefined) { 
  // unreachable
@pocesar
Copy link
Author

@pocesar pocesar commented Apr 4, 2019

something weird happened just now, I had an event called "onSelect", but typescript accepted the global "onselect" (from window.onselect). another one that bit me. it's "null" by default, and my props were | null, took me a while to find this, because it was not showing any console.log statements, and since the same event was being used elsewhere, the "no unused locals" didn't help me spot it

@pocesar
Copy link
Author

@pocesar pocesar commented Apr 17, 2019

another one happened to me, my electron window was being closed after my react element was unmounting, I had this piece of code:

  const closeAll = useCallback(() => {
    while (unpipes.current.length) {
      const p = unpipes.current.shift()
      if (p) {
        p()
      }
    }

    while (conn.current.length) {
      const p = conn.current.shift()
      if (p && p.close) {
        p.close()
      }
    }
  }, [conn, unpipes])

  useEffect(() => {
    return close
  }, [close])

can you spot the issue? I couldn't until wasting 8 hours trying to fix "the unfixable". close is window.close, and was executing when my component unmounted. the closeAll function was named close before, because it was a single ref and not a ref of an array with registered cleanup callbacks...

on lib.dom.d.ts
image
vscode highlight
image

the funny part is that I'm also using tslint, and it went unnoticed

@ericanderson
Copy link
Contributor

@ericanderson ericanderson commented Jul 1, 2019

Perhaps we could get a compiler flag for noImplicitGlobalThis? Thus using name would be an error but if you want to use globalThis.name you could?

@NiclasHansson
Copy link

@NiclasHansson NiclasHansson commented Jul 4, 2019

Forgot to destruct my variable 'name' from props and ended up using the global name variable instead. To me it's obviously a very bad idea to have globals with such common variable names as 'name' and 'length'.

Any plans on removing them and letting people access them through window instead? Or have they been already in later versions (I'm using v2.8.3)

@pocesar
Copy link
Author

@pocesar pocesar commented Jul 4, 2019

the issue is much bigger than it appears to be, since it can ship broken software. even with all the strict functions enabled, and using tslint as well...

@hyurl
Copy link

@hyurl hyurl commented Jul 4, 2019

This is an obvious BUG, admit it

@inca
Copy link

@inca inca commented Sep 18, 2019

I agree with this being a bug, even with name being defined as never one can still write code which compiles and results in runtime issues. Consider following examples:

// ------- 1 -------
interface Foo {
    foo: object;
}
const foo: Foo = {
    foo: name
};

// ------- 2 -------
function a(foo: string) {}
a(name);

// ------- 3 -------
function b(params: {foo: string}) {}
b({ foo: name });

All 3 compiles perfectly. All 3 should be compile-time errors instead (not linting errors!) because they are directly related to symbol/type resolution (which is compiler's domain).

@jchv
Copy link

@jchv jchv commented Sep 18, 2019

I am still unsure why unknown wouldn’t be a better type than never.

@pocesar
Copy link
Author

@pocesar pocesar commented Sep 18, 2019

this issue missed the timing for a breaking change before wide adoption of TS... we have dom.iterable, that you must provide if you want to iterate using document.querySelector[All], but we don't have dom.globals...

@Kingwl
Copy link
Member

@Kingwl Kingwl commented Dec 23, 2019

Same on this one🤦🏻‍♂️

@pocesar
Copy link
Author

@pocesar pocesar commented Jan 22, 2020

just got bitten again by "status" (that comes from Promise.allSettled callback), it's a global variable...

@telamonian
Copy link

@telamonian telamonian commented Jan 29, 2020

I'll add my voice to the chorus here. There's a line in my code that's used to fetch icon objects based on icon names:

const resolved = LabIcon._instances.get(name);

where name is a local variable that stores icon name. Just now I refactored name => icon, but forgot to change the line above. And then all of the icons in my app broke.

Took me 10 minutes to trace it back, and then another 10 minutes of googling to find out why my compiler hadn't noisily failed/to find this thread.

@hyurl
Copy link

@hyurl hyurl commented Jan 29, 2020

name variable comes from browsers (DOM), so if the --lib compile option doesn't contain any DOM version, allowing name to be available is definitely a BUG, I don't know why the core team just can't admit it. I don't believe anyone would potentially use this variable in Node.js environment without knowing what he is doing, concerning about losing compatibility of removing this variable is unreasonable.

PS: this issue had been open since 2017, it's now 2020, even if someone accidentally used the variable in history, it should be all faded out by now.

PPS: new versions of TypeScript did break some compatibilities, for instance, the IterableIterator interface, I don't see anybody complaining about it, people just think TypeScript is getting better and more accurate.

@tomboland-vocovo
Copy link

@tomboland-vocovo tomboland-vocovo commented Feb 10, 2020

I've just been bitten by this, and I'm not even writing TS, I'm using VSCode's type-checking support for plain JS. I was very suprised to find status was seemingly completely OK, due to being defined in lib.dom-globals.d.ts, and I introduced a bug where I should've been referring to statusCode defined a couple of lines above!

@thatodieguyspj
Copy link

@thatodieguyspj thatodieguyspj commented Apr 1, 2020

The current functionality has allowed some bugs in our codebase. "name", "length" & "event" being global variables seems like ancient technology to me. Is anybody using TypeScript and still accessing these?

Here's our workaround using an ESLint rule:
"no-restricted-globals": ["error", "closed", "event", "fdescribe", "name", "length", "location", "parent", "top", ],

@joshuakb2
Copy link

@joshuakb2 joshuakb2 commented Jul 14, 2020

This just got me in a node project. ReferenceError: name is not defined. I'll be using @thatodieguyspj's ESLint rule, thanks for that!

@pikeas
Copy link

@pikeas pikeas commented Sep 9, 2020

Experienced dev new to Typescript, just bit by this. I have no position on how this should be improved, but the current behavior is a bad dev experience to the point where it should absolutely be changed.

Names like, well, "name", are extremely common; it's a massive footgun for code to compile successfully and then error at runtime due to what is substantially an undefined variable, even if this global type definition makes sense in the historical context.

@hyurl
Copy link

@hyurl hyurl commented Sep 9, 2020

If anyone would argue that changing this may lead to unexpected errors for historical projects, I'd like to say that those projects are buggy by themselves.

@Jack-Works
Copy link
Contributor

@Jack-Works Jack-Works commented Sep 10, 2020

We keep going around on this and need to come up with something that really solves the issue

@RyanCavanaugh

With PR #40402, we can type those variables as declare const name: throw "using of global deprecated 'name'" and once people use that accidentally console.log(name), it will immediately generate a diagnostic instead of resolving to a never type. How do you think?

@Jack-Works
Copy link
Contributor

@Jack-Works Jack-Works commented Sep 10, 2020

image

#40468 but it requires changes in libdom.d.ts and not easy to i18n

@pocesar
Copy link
Author

@pocesar pocesar commented Sep 10, 2020

@sandersn this shouldn't be closed, there are many globals that should be changed

@orta orta reopened this Sep 10, 2020
@orta
Copy link
Member

@orta orta commented Sep 10, 2020

I'm cool with keeping it open (it auto-closed) for discussion about the rest of the values, name I think is definitely the worst-case offender in there. Also throw types are cool.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.