# What's new in TypeScript 5.0?

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](https://devblogs.microsoft.com/typescript/announcing-typescript-1-5/). 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](https://github.com/tc39/proposal-decorators). At [this stage](https://tc39.es/process-document/), 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`](https://www.typescriptlang.org/tsconfig#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`](https://www.typescriptlang.org/tsconfig#emitDecoratorMetadata) is not compatible at all.

### How do they look?

TypeScript's Decorator type signature looks as follows:

```typescript
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`, and `accessor`
    
* `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 private
    
* `static` if set to true, decorated entity is static
    
* `addInitializer` 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:

```typescript
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:

```typescript
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:

```typescript
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`:

```typescript
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:

```typescript
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:

```typescript
// 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](https://devblogs.microsoft.com/typescript/announcing-typescript-5-0-rc?ref=malja-dev)

[https://github.com/tc39/proposal-decorators](https://github.com/tc39/proposal-decorators?ref=malja-dev)
