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

Feedback: 5.0 deprecations #51909

Closed
RyanCavanaugh opened this issue Dec 15, 2022 · 34 comments
Closed

Feedback: 5.0 deprecations #51909

RyanCavanaugh opened this issue Dec 15, 2022 · 34 comments
Labels
Discussion Issues which may not have code impact

Comments

@RyanCavanaugh
Copy link
Member

RyanCavanaugh commented Dec 15, 2022

See also #51000 / #51424

In 5.0, we've marked the following configurations as deprecated:

  • target: ES3
  • importsNotUsedAsValues
  • noImplicitUseStrict
  • keyofStringsOnly
  • preserveValueImports
  • suppressExcessPropertyErrors
  • suppressImplicitAnyIndexErrors
  • noStrictGenericChecks
  • charset
  • out
  • prepend in project references
  • implicitly OS-specific newLine

These configurations will continue to "work" until TypeScript 5.5, at which point they will be removed entirely. In TypeScript 4.9.5+, 5.0, 5.1, 5.2, 5.3, and 5.4, you can specify ignoreDeprecations: "5.0" to silence this warning.

What should I use instead?

target: ES3

ES3 runtimes are nearly 100% extinct, so we're removing support for this as an emit target.

Fix: Use target: ES5 as the next-closest thing along with a linter to detect any potentially-problematic ES3 constructs, or use target: ESNext and use a different downleveling emitter.

noImplicitUseStrict

This flag disables emitting a "use strict"; prologue at the top of module bodies when the target is not implicitly a module. This can lead to unexpected behavior since there are subtle behavioral differences between strict and nonstrict mode.

Fix: Remove any reliance on nonstrict behavior.

keyofStringsOnly

This flag removes symbol-based keys from the keyof T type operator. This can lead to unexpected type errors when code relying on this flag interacts with code that doesn't.

Fix: Use string & keyof T to filter keys to those with string types.

suppressExcessPropertyErrors

This flag disables excess property checking. This can lead to unexpected program behavior when an optional property name is misspelled, since no error is issued.

Fix: use a type assertion to silence this in cases where you really do want an optional property, or fix the target type.

suppressImplicitAnyIndexErrors

This flag disables the error produced when indexing an object which doesn't support indexing. This was always intended to be a temporary flag, so is going away.

Fix: Add an index signature to the relevant type (or use a type assertion at the indexing location).

noStrictGenericChecks

This flag disables certain checks relating the constraints of generic types, which can lead to undesirable unsound behavior.

Fix: Correct the types, or use a type assertion.

charset

This flag hasn't done anything for a very long time.

Fix: Remove it from your config file

out

This flag for emitting a single file from a set of nonmodule inputs incorrectly relied on the current working directory to compute the output filename, leading to unexpected problems when tsc was invoked programmatically or from a different directory.

Fix: use outFile instead.

prepend in project references

This setting allowed for upstream project reference outputs to be prepended to the current project's output files. Had we not needed this ourselves back in 3.0, we likely wouldn't have added it in the first place, and it hasn't seen wide adoption.

Fix: Use a single tsconfig to create output bundles, or use an external bundler on the outputs.

Implicitly OS-specific newLine

The newLine setting controls the line endings used in emitted files. If not specified, in TypeScript 4.9, this would default to the operating system's idiomatic newline (CR LF for Windows, LF for everyone else). Since all modern dev tools support LF endings without issue, we're removing this behavior to increase build predictability, and the new default is LF.

Fix: Pick one or the other, or use a tool after-the-fact to change outputs to the current OS's if this is really desired

Feedback?

We believe we've picked a fairly conservative set of options to deprecate in this release. If these deprecations are overly burdensome for your codebase, we'd like to understand why - please let us know!

Search terms: Flag is deprecated and will stop functioning in TypeScript 5.5. Specify ignoreDeprecations: 5.0 to silence this error

@RyanCavanaugh RyanCavanaugh added the Discussion Issues which may not have code impact label Dec 15, 2022
@fatcerberus
Copy link

If noImplicitUseStrict is to be deprecated, shouldn’t the out-of-box behavior be to not emit the directive if the target is not a module? Plain JS isn’t automatically strict in non-module code so it doesn’t feel like TS should be imposing it under the current design principles, as that changes the behavior of the code as written compared to JS. (no matter how bad an idea I think it is to ever write sloppy-mode code)

@fatcerberus
Copy link

suppressExcessPropertyErrors

TIL this flag exists

@RyanCavanaugh
Copy link
Member Author

RyanCavanaugh commented Dec 15, 2022

If noImplicitUseStrict is to be deprecated, shouldn’t the out-of-box behavior be to not emit the directive if the target is not a module?

So the issue is that if you have the input code

export function fn() {
  if (this === undefined) {
    console.log('called free');
  } else {
    console.log('called bound');
  }
}
fn();
({ fn }).fn();

Per spec, this must print "called free, called bound", because all module code is in strict mode, and this must be a module file (it has a top-level export declaration), but without "use strict"; this prints "called bound, called bound". Removing this flag is basically saying you can't opt into this particular out-of-spec behavior.

To clarify, this doesn't change any behavior when the input file is not a module.

@fatcerberus
Copy link

To clarify, this doesn't change any behavior when the input file is not a module.

thanks, this was the part I was missing.

@nmain
Copy link

nmain commented Dec 16, 2022

Will any-indexing still be allowed when "noImplicitAny": false is set?

@RyanCavanaugh
Copy link
Member Author

Will any-indexing still be allowed when "noImplicitAny": false is set?

Yes

@remcohaszing
Copy link

Personally I find the default behaviour of newLine weird. TypeScript compile output now depends on the build system operating system. I would love to see that option deprecated, default to "lf" (even crlf is fine, as long as it’s consistent), and ideally removed in a future version.

Similarly I think forceConsistentCasingInFileNames should be true by default, deprecated, and removed later. I see no reason to not support case sensitive file systems by default.

@RyanCavanaugh
Copy link
Member Author

That's good feedback, I'll raise it as a possibility

@RyanCavanaugh
Copy link
Member Author

#52298

@dummdidumm
Copy link

The list is missing importsNotUsedAsValues and preserveValueImports.

As a general feedback, I'm having a headache currently how to make the deprecations go away in a backwards-compatible way. For context, in SvelteKit we're creating a generated tsconfig.json which could be used for versions prior or after 5.0. We could check ts.version, but that's only the TypeScript version the user has installed, not necessarily the VS Code TS version (as can be seen by this issue sveltejs/kit#8650).
Ideally it would be possible to set ignoreDeprecations as a flag prior to 5.0 (e.g. backporting it to 4.9/4.8) - though that ship probably has probably sailed already since it's no use for the VS Code problem: there won't be releases for older TS versions and users on old VS Code versions won't update. But it's probably good for the future to allow to set deprecation values like 6.0 in versions before 6.0 (maybe starting with 5.5).

@RyanCavanaugh
Copy link
Member Author

As a general feedback, I'm having a headache currently how to make the deprecations go away in a backwards-compatible way.

I'm sympathetic and we might want to ship a 4.9 patch release to allow ignoreDeprecations to be specified without effect, but also a little confused because this compatibility story has never really existed: every time we've introduced a new compilerOptions flag, it's only been legal to use that flag in the version of TypeScript it was introduced in. So at no point could you have an arbitrary N+1-versioned tsconfig and use in in the N-versioned tsc.

@dummdidumm
Copy link

dummdidumm commented Jan 26, 2023

That's true, it never was legal before, but it also never occured before that a flag became deprecated and resulted in an error, so you were able to use your unchanged tsconfig across (major) versions.

@RyanCavanaugh
Copy link
Member Author

Yep, good point. We've merged #52419 so will ship a patch release of 4.9 shortly that will allow specifying ignoreDeprecations.

@nick-hill-dev
Copy link

What specific changes need to be made to a tsconfig.json file to include the output of a project reference in an outputted file?

My .tsconfig for example currently looks like this:

{ "compileOnSave": true, "compilerOptions": { "target": "ES2021", "module": "system", "outFile": "teranova.js", // etc }, "references": [ { "path": "../../Javascript/Canvas", "prepend": true }, // etc ] }

@RyanCavanaugh
Copy link
Member Author

What specific changes need to be made to a tsconfig.json file to include the output of a project reference in an outputted file?

The functionality is going away entirely. You look into something like rollup, parcel, or just straight concatenation with a script.

Side observation: to be honest, using prepend with system+outFile was never even considered as a scenario when that feature was implemented, so the fact that worked in the first place is kind of a happy accident at best 😲

@nick-hill-dev
Copy link

Well that's quite a badge of honour to be told I've been doing it wrong successfully for about seven years! 😲 I've got a custom development process where I clone various repositories (I.E. my libraries) into a sort of workspace and combine them into a single .js file for local testing, and deployment onto the web, often as SPAs which are essentially games oriented around a canvas HTML object. Just out of interest, what is the typical way a developer should be going about this? As in, to clone multiple repositories (where one of them is the app itself) and build them all and combine them all together for local testing and deployment? I understand that there are various options but obviously after your comment I'm keen to finally understand what a normal person would do!

@nick-hill-dev
Copy link

Just to add, in case it makes it more obvious what I'm talking about, I had been approacing software development with TypeScript having come from a background of working in C# and Visual Studio, so my development process is as if I am creating a solution consisting of multiple projects which have been included in the solution and then when I build it produces a single executable.

@remcohaszing
Copy link

Maybe it would also be good to switch the default module compiler option to node16. This is fairly impactful to users, but it is the most correct value in most cases. I believe many users are still not even aware of this option, causing packages to be stuck on not supporting it at all.

@RyanCavanaugh
Copy link
Member Author

📰: TypeScript 4.9.5 is now out, which allows specifying "ignoreDeprecations": "5.0"

@HolgerJeromin
Copy link
Contributor

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Feb 2, 2023

Weird - the deploy timed out for just the NuGet build.

4.9.5 should be out on NuGet as well now!

@reverofevil
Copy link

@frost-cy Hello! Will there be a "google feedback" ticket for 5.0? Sorry for a ping.

@frost-cy
Copy link

frost-cy commented Feb 4, 2023

@polkovnikov-ph yes, @mhausner is working on it.

@jakebailey
Copy link
Member

That's just a bug; I would definitely file that as a real issue.

evilchia added a commit to cydran/cydran that referenced this issue Mar 17, 2023
charset no longer used - see microsoft/TypeScript#51909
matthewwerny pushed a commit to cydran/cydran that referenced this issue Mar 22, 2023
* dependency update

* @babel/core: 7.21.3
* @types/jest: 29.5.0
* dependency-cruiser: 12.10.1
* jsdom: 21.1.1
* rimraf: 4.4.0
* typedoc: 0.23.27
* typescript: 5.0.2

* Deprecated flag

charset no longer used - see microsoft/TypeScript#51909

* deprecated attribute

see:
* https://www.typescriptlang.org/tsconfig#suppressImplicitAnyIndexErrors
* microsoft/TypeScript#51000

* types to be explicitly considered while other types not included

* new dev dependency to remove @types usage confusion during transpilation

* current lock file
@michkot
Copy link

michkot commented Jun 27, 2023

o.stream = (process as any)[o.stream]

I find that quite dangerous, since process could be null, and we are muting possible null-access error with the "as any"...

I wandered here, as we have a lot of lines that should ideally look like:
x?.[Symbol.toStringTag] === "someConstant"
however x is actually unknown, so we need to cast it to something... and if you choose any as that something (which our linter will hate) , it is very easy to miss the possible null-access error.

So some of the safer but quite verbose alternatives are:

  • (x as Record<keyof never, unknown> | undefined)?.[Symbol.toStringTag] and
  • (obj as { [Symbol.toStringTag]: unknown})?.[Symbol.toStringTag]

I was wondering if suppressImplicitAnyIndexErrors would help us, but it is being deprecated -> could you recommend some not-so-verbose but reasonably safe alternative to (x as any)?.[Symbol.toStringTag]

@mlippert
Copy link

@michkot

o.stream = (process as any)[o.stream]

I find that quite dangerous, since process could be null, and we are muting possible null-access error with the "as any"...

Perhaps, but all the cast is doing is making explicit what would otherwise have been an implicit cast that was suppressed by the now deprecated suppressImplicitAnyIndexErrors for the code in question.

I think that if suppressImplicitAnyIndexErrors would have helped you, then casting to any would have the same effect.
It doesn't really matter what you cast to if you're testing for null before access, which the ?. is doing then you won't have a null-access error.

@WirelessMistress
Copy link

ES3 happens to be the version under which you will be scripting Adobe After Effects
You Mean to tell me that ES3 runtimes are Extinct with all these people scripting
the Adobe Creative Suite ... stuff Like After Effects ...Premiere Pro ... Photoshop ... and Illustrator.
You obviously are not looking at these people or ... may I say it directly ... at the right spot ... it is a pretty big statement you made ... if you ask me

@WirelessMistress
Copy link

ExtendScript by Adobe is an Extension of ES3

@RyanCavanaugh
Copy link
Member Author

As of Version 16, which came out 6 years ago, the After Effects scripting engine has moved to ES2018. The ExtendScript version is considered legacy.

@DanielRosenwasser
Copy link
Member

@dummdidumm I've added those to the list for anyone else reading.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Discussion Issues which may not have code impact
Projects
None yet
Development

No branches or pull requests