On the 1st of March, the team behind TypeScript released TypeScript 5.0 RC. That means the version is pretty stable and we can have a look at some new features, Let's roll!
Decorators
Decorators are part of TypeScript for a long time. However, they are still experimental and non-standard (meaning decorators are not part of the ECMAScript standard) features.
Version 5.0 brings us the implementation of the Stage 3 proposal. At this stage, the standard is almost completed and no further changes should be made.
experimentalDecorators
No More
From now on, decorators are a valid part of the code. This means, that you no longer need to set experimentalDecorators
to true
. The flag stays in, however, new decorators are emitted and type-checked differently. As a result, old decorators won't work properly! In addition, flag emitDecoratorMetadata
is not compatible at all.
How do they look?
TypeScript's Decorator type signature looks as follows:
type Decorator = (
value: DecoratedValue,
context: {
kind: string;
name: string | symbol;
addInitializer(initializer: () => void): void;
static: boolean;
private: boolean;
access: {get: () => unknown, set: (value: unknown) => void};
}
) => void | ReplacementValue;
As you can see, a decorator is just a function, that takes two parameters - value
and context
.
Decorated entity
value
contains the decorated entity. That may be class, method, getter/setter, field, or accessor.
Context
context
adds additional data about the decorated entity:
kind
tells us the type of decorated entity. It may contain the following values:class
,method
,getter
,setter
,field
, andaccessor
name
is the name of the decorated entity (class name, method name, ...)access
contains get/set methods for the entity.private
if set to true, decorated entity is privatestatic
if set to true, decorated entity is staticaddInitializer
allows the decorator to add additional initialization logic.
Not all fields are accessible all the time. The context
object changes based on the type of decorated entity:
type Decorator =
| ClassDecorator
| ClassMethodDecorator
| ClassGetterDecorator
| ClassSetterDecorator
| ClassAutoAccessorDecorator
| ClassFieldDecorator
const
Type Parameters
When you don't provide a return type, TS has to guess (infer) it. In the previous versions, TypeScript always chose the closest "general" type:
type RequestStatus = {
code: readonly number
message: readonly string
}
function getRequestCode<T extends RequestStatus>(status: T): T['code'] {
return status.code
}
const code = getRequestCode({ code: 404, message: 'Not Found' })
// ^ inferred type: number ❌
Sometimes it's desirable, to get a more specific (in this case readonly) type. One way to do so is by adding as const
when passing the parameter into getRequestCode
function:
const code = getRequestCode({ code: 404, message: 'Not Found` } as const )
// ^ inferred type: 404 ✅
But that shifts responsibility to the user of your code and is easy to forget to do. Instead, with TS 5.0, you can set the parameter type as const
:
function getRequestCode<const T extends RequestStatus>(status: T): T['code'] {
return status.code
}
const code = getRequestCode({ code: 404, message: 'Not Found` })
// ^ inferred type: 404 ✅
At the same time, const
modifier does not require immutable values:
function stillReturnsMutable<const T extends readonly string[]>(param: T): T {
return param
}
const values = ['Hoy', 'Holla']
// ^ mutable
const result = stillReturnsMutable(values)
// ^ inferred type: string[]
bundler
Module Resolution
module
options nodenext
and node16
required all relative imports to include a file extension:
// index.mjs
import * from './otherFile' // ❌ TS is missing file extension
import * from './otherFile.mjs' // ✅ works as expected
Most bundlers (Vite, Parcel, esbuild ...) support a hybrid lookup strategy, which finds both files with and without the extension. That conflicted with the TS way described above.
The new bundler
option for module
allows TypeScript to do the same as bundlers - finding the file even without the extension.
Further reading
This post was by no means an extensive list of all changes but a brief introduction. If you are interested in the full list of changes, see the following links.
https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-rc