精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

TypeScript 5.2 發布,支持顯式資源管理!

開發 后端
TypeScript 5.2 添加了對 ECMAScript 中即將推出的顯式資源管理功能的支持。創建對象后通常需要進行某種“清理”。 例如,可能需要關閉網絡連接、刪除臨時文件或只是釋放一些內存空間。

根據 TypeScript 路線圖,TypeScript 5.2 計劃于 8.22 發布。下面就來看看該版本都帶來了哪些新特性!

以下是 TypeScript 5.2 新增的功能:

  • using 聲明和顯式資源管理
  • 裝飾器元數據
  • 命名和匿名元組元素
  • 聯合類型數組方法調用
  • 對象成員的逗號自動補全
  • 內聯變量重構
  • 重大變更和正確性修復

using 聲明和顯式資源管理

TypeScript 5.2 添加了對 ECMAScript 中即將推出的顯式資源管理功能的支持。

創建對象后通常需要進行某種“清理”。 例如,可能需要關閉網絡連接、刪除臨時文件或只是釋放一些內存空間。

假如有一個函數,它創建了一個臨時文件,通過各種操作讀寫該文件,然后關閉并刪除它。

import * as fs from "fs";

export function doSomeWork() {
    const path = ".some_temp_file";
    const file = fs.openSync(path, "w+");

    // 操作文件...

    // 關閉文件并刪除它
    fs.closeSync(file);
    fs.unlinkSync(path);
}

那如果需要提前退出怎么辦?

export function doSomeWork() {
    const path = ".some_temp_file";
    const file = fs.openSync(path, "w+");

    // 操作文件...
    if (someCondition()) {
        // 其他操作...

        // 關閉文件并刪除它
        fs.closeSync(file);
        fs.unlinkSync(path);
        return;
    }

    // 關閉文件并刪除它
    fs.closeSync(file);
    fs.unlinkSync(path);
}

可以看到,這里就出現了重復的清理工作。如果拋出錯誤,我們也不保證關閉并刪除文件。這可以通過將這一切包裝在 try/finally 塊中來解決。

export function doSomeWork() {
    const path = ".some_temp_file";
    const file = fs.openSync(path, "w+");

    try {
        // 操作文件...

        if (someCondition()) {
            // 其他操作...
            return;
        }
    }
    finally {
        // 關閉文件并刪除它
        fs.closeSync(file);
        fs.unlinkSync(path);
    }
}

這樣寫雖然沒有什么大問題,但是會讓代碼變得復雜。如果我們向finally塊添加更多清理邏輯,還可能遇到其他問題,比如異常阻止了其他資源的釋放。 這就是顯式資源管理提案旨在解決的問題。該提案的關鍵思想是將資源釋放(要處理的清理工作)作為 JavaScript 中的一等公民來支持。

這一功能的實現方式是引入一個名為 Symbol.dispose 的新內置 symbol,并且可以創建具有以 Symbol.dispose 命名的方法的對象。為了方便起見,TypeScript 定義了一個名為 Disposable 的新全局類型,用于描述這些對象。

class TempFile implements Disposable {
    #path: string;
    #handle: number;

    constructor(path: string) {
        this.#path = path;
        this.#handle = fs.openSync(path, "w+");
    }

    // 其他操作

    [Symbol.dispose]() {
        // 關閉文件并刪除它
        fs.closeSync(this.#handle);
        fs.unlinkSync(this.#path);
    }
}

然后,可以調用這些方法:

export function doSomeWork() {
    const file = new TempFile(".some_temp_file");

    try {
        // ...
    }
    finally {
        file[Symbol.dispose]();
    }
}

將清理邏輯移到 TempFile 本身并不能帶來很大的好處;基本上只是將所有的清理工作從 finally 塊中移到一個方法中,而這之前這就是可以實現的。但是,有一個眾所周知的方法名稱意味著 JavaScript 可以在其之上構建其他功能。

這就引出了該特性的第一個亮點:使用 using 聲明!using 是一個新的關鍵字,可以聲明新的固定綁定,有點類似于 const。關鍵的區別在于,使用 using 聲明的變量在作用域結束時會調用其 Symbol.dispose 方法!

所以我們可以簡單地編寫這段代碼:

export function doSomeWork() {
    using file = new TempFile(".some_temp_file");

    // 操作文件...

    if (someCondition()) {
        // 其他操作...
        return;
    }
}

可以看到,已經沒有了  try/finally 塊,從功能上來說,這就是 using 聲明為我們做的事情,但我們不必處理其中的細節。

如果你對 C# 的 using 聲明、Python 的 with 語句或者 Java 的 try-with-resource 聲明比較熟悉。就會發現,JavaScript 的新關鍵字 using 和它們類似,并提供了一種顯式的在作用域結束時執行對象的 "清理" 操作的方式。

**using**** 聲明會在其所屬的作用域的最后,或者在出現 "早期返回"(如 return 或拋出錯誤)之前進行清理操作。它們還按照先進后出的順序(類似于棧)進行釋放。

function loggy(id: string): Disposable {
    console.log(`Creating ${id}`);

    return {
        [Symbol.dispose]() {
            console.log(`Disposing ${id}`);
        }
    }
}

function func() {
    using a = loggy("a");
    using b = loggy("b");
    {
        using c = loggy("c");
        using d = loggy("d");
    }
    using e = loggy("e");
    return;

    // 不會執行
    // 不會創建,不會處置
    using f = loggy("f");
}

func();
// Creating a
// Creating b
// Creating c
// Creating d
// Disposing d
// Disposing c
// Creating e
// Disposing e
// Disposing b
// Disposing a

使用 using 聲明的特點之一是在處理資源時具有異常處理的彈性。當使用聲明結束時,如果發生了錯誤,該錯誤將在資源釋放后重新拋出。這樣可以確保資源在發生異常的情況下也能被正確地釋放。在函數體內部,除了常規邏輯可能拋出錯誤外,Symbol.dispose 方法本身也可能會拋出錯誤。如果在資源釋放期間發生了錯誤,那么這個錯誤也會被重新拋出。

但是,如果在清理之前和清理期間的邏輯都拋出錯誤會怎樣呢?為了處理這些情況,引入了一個新的 Error 子類型,名為 SuppressedError。它具有一個 suppressed 屬性,用于保存最后拋出的錯誤,以及一個 error 屬性,用于保存最近拋出的錯誤。

當在處理資源時發生多個錯誤時,SuppressedError 類型的錯誤對象可以保留最新的錯誤,并將之前發生的錯誤標記為被壓制的錯誤。這種機制允許我們更好地跟蹤和處理多個錯誤的情況。

假設在進行資源清理時,首先發生了一個錯誤A,然后又發生了一個錯誤B。在使用 SuppressedError 錯誤對象時,它會將錯誤B作為最新錯誤記錄,并將錯誤A標記為被壓制的錯誤。這樣一來,我們可以通過 SuppressedError 對象獲取到兩個錯誤的相關信息,從而更全面地了解發生的錯誤情況。

class ErrorA extends Error {
    name = "ErrorA";
}
class ErrorB extends Error {
    name = "ErrorB";
}

function throwy(id: string) {
    return {
        [Symbol.dispose]() {
            throw new ErrorA(`Error from ${id}`);
        }
    };
}

function func() {
    using a = throwy("a");
    throw new ErrorB("oops!")
}

try {
    func();
}
catch (e: any) {
    console.log(e.name); // SuppressedError
    console.log(e.message); // An error was suppressed during disposal.

    console.log(e.error.name); // ErrorA
    console.log(e.error.message); // Error from a

    console.log(e.suppressed.name); // ErrorB
    console.log(e.suppressed.message); // oops!
}

可以看到,這些示例中都使用了同步方法。 但是,許多資源處置涉及到異步操作,我們需要等待這些操作完成才能繼續運行其他代碼。

因此,還引入了一個名為 Symbol.asyncDispose 的新 symbol,并且帶來了一個新特性:await using 聲明。它們與 using 聲明類似,但區別在于它們會查找需要等待其釋放的對象。它們使用由 Symbol.asyncDispose 命名的不同方法,盡管也可以操作具有 Symbol.dispose 的對象。為了方便起見,TypeScript 還引入了一個全局類型 AsyncDisposable,用于描述具有異步釋放方法的對象。

async function doWork() {
    await new Promise(resolve => setTimeout(resolve, 500));
}

function loggy(id: string): AsyncDisposable {
    console.log(`Constructing ${id}`);
    return {
        async [Symbol.asyncDispose]() {
            console.log(`Disposing (async) ${id}`);
            await doWork();
        },
    }
}

async function func() {
    await using a = loggy("a");
    await using b = loggy("b");
    {
        await using c = loggy("c");
        await using d = loggy("d");
    }
    await using e = loggy("e");
    return;

    // 不會執行
    // 不會創建,不會處置
    await using f = loggy("f");
}

func();
// Constructing a
// Constructing b
// Constructing c
// Constructing d
// Disposing (async) d
// Disposing (async) c
// Constructing e
// Disposing (async) e
// Disposing (async) b
// Disposing (async) a

使用 Disposable 和 AsyncDisposable 來定義類型可以使代碼更易于處理。實際上,許多已存在的類型都具有 dispose() 或 close() 方法,這些方法用于資源清理。例如,Visual Studio Code 的 API 甚至定義了它們自己的 Disposable 接口。瀏覽器和像 Node.js、Deno、Bun 這樣的運行時中的 API 也可以選擇為已經具有清理方法的對象使用 Symbol.dispose 和 Symbol.asyncDispose。

也許這對于庫來說聽起來很不錯,但對于一些場景來說可能有些過重。如果需要進行大量的臨時清理工作,創建一個新的類型可能會引入過多的抽象。例如,再看一下上面 TempFile 的例子:

class TempFile implements Disposable {
    #path: string;
    #handle: number;

    constructor(path: string) {
        this.#path = path;
        this.#handle = fs.openSync(path, "w+");
    }

    // 其他操作

    [Symbol.dispose]() {
        // 關閉文件并清理它
        fs.closeSync(this.#handle);
        fs.unlinkSync(this.#path);
    }
}

export function doSomeWork() {
    using file = new TempFile(".some_temp_file");

    // 操作文件...

    if (someCondition()) {
        // 其他操作...
        return;
    }
}

我們只是想記住調用兩個函數,但是這種寫法是最好的嗎?我們應該在構造函數中調用 openSync、創建一個 open() 方法,還是自己傳入處理方法?應該為每個可能的操作都暴露一個方法,還是將屬性設為 public?

這就引出了新特性的主角:DisposableStack 和 AsyncDisposableStack。這些對象非常適用于一次性的清理操作,以及任意數量的清理工作。DisposableStack 是一個對象,它具有多個用于跟蹤 Disposable 對象的方法,并且可以接收執行任意清理工作的函數。同時,我們也可以將 DisposableStack 分配給 using 變量,這意味著我們可以將其用于資源管理,并在使用完成后自動釋放資源。這是因為 DisposableStack 本身也實現了 Disposable 接口,所以可以像使用其他 Disposable 對象一樣使用它。

下面是改寫原例子的方式:

function doSomeWork() {
    const path = ".some_temp_file";
    const file = fs.openSync(path, "w+");

    using cleanup = new DisposableStack();
    cleanup.defer(() => {
        fs.closeSync(file);
        fs.unlinkSync(path);
    });

    // 操作文件...

    if (someCondition()) {
        // 其他操作...
        return;
    }

    // ...
}

這里的defer() 方法接受一個回調函數,該回調函數將在清理被釋放時運行。通常情況下,defer()(以及其他類似的 DisposableStack 方法,如 use 和 adopt)應該在創建資源后立即調用。正如其名稱所示,DisposableStack 以棧的方式處理它所跟蹤的所有內容,按照先進后出的順序進行清理,因此立即在創建值后延遲執行可幫助避免奇怪的依賴問題。AsyncDisposable 的工作原理類似,但可以跟蹤異步函數和 AsyncDisposable,并且本身也是一個 AsyncDisposable。

由于這個特性非常新,大多數運行時環境不會原生支持它。要使用它,需要為以下內容提供運行時的 polyfills:

  • Symbol.dispose
  • Symbol.asyncDispose
  • DisposableStack
  • AsyncDisposableStack
  • SuppressedError

然而,如果你只關注使用 using 和 await using,只需要提供內置 symbol 的 polyfill 就足夠了。對于大多數情況,下面這樣簡單的實現應該可以工作:

Symbol.dispose ??= Symbol("Symbol.dispose");
Symbol.asyncDispose ??= Symbol("Symbol.asyncDispose");

此外,還需要將編譯目標設置為 es2022 或更低,并在 lib 設置中配置 "esnext" 或 "esnext.disposable"。

{
  "compilerOptions": {
    "target": "es2022",
    "lib": ["es2022", "esnext.disposable", "dom"]
  }
}

裝飾器元數據

TypeScript 5.2 實現了一個即將推出的 ECMAScript 功能,稱為裝飾器元數據。這個功能的關鍵思想是讓裝飾器能夠在它們所用于或內嵌的任何類上輕松創建和使用元數據。

無論何時使用裝飾器函數,它們都可以在上下文對象的新metadata屬性上進行訪問。metadata屬性僅包含一個簡單的對象。由于JavaScript允許我們任意添加屬性,它可以被用作一個由每個裝飾器更新的字典。另外,由于每個裝飾部分的元數據對象將對于類的每個裝飾部分都是相同的,它可以作為一個Map的鍵。當類上的所有裝飾器都執行完畢后,可以通過Symbol.metadata從類上訪問該對象。

interface Context {
    name: string;
    metadata: Record<PropertyKey, unknown>;
}

function setMetadata(_target: any, context: Context) {
    context.metadata[context.name] = true;
}

class SomeClass {
    @setMetadata
    foo = 123;

    @setMetadata
    accessor bar = "hello!";

    @setMetadata
    baz() { }
}

const ourMetadata = SomeClass[Symbol.metadata];

console.log(JSON.stringify(ourMetadata));
// { "bar": true, "baz": true, "foo": true }

這在許多不同的場景中都非常有用。元數據可以用于許多用途,例如調試、序列化或者在使用裝飾器進行依賴注入時。由于每個被裝飾的類都會創建相應的元數據對象,框架可以將它們作為私有的鍵入到 Map 或 WeakMap 中,或者根據需要添加屬性。

舉個例子,假設想要使用裝飾器來跟蹤哪些屬性和訪問器在使用JSON.stringify進行序列化時是可序列化的,代碼示例如下:

import { serialize, jsonify } from "./serializer";

class Person {
    firstName: string;
    lastName: string;

    @serialize
    age: number

    @serialize
    get fullName() {
        return `${this.firstName} ${this.lastName}`;
    }

    toJSON() {
        return jsonify(this)
    }

    constructor(firstName: string, lastName: string, age: number) {
        // ...
    }
}

這里的意圖是只有 age 和 fullName 應該被序列化,因為它們標記了 @serialize 裝飾器。我們為此定義了一個 toJSON 方法,但它只是調用了 jsonify,后者使用了 @serialize 創建的元數據。

const serializables = Symbol();

type Context =
    | ClassAccessorDecoratorContext
    | ClassGetterDecoratorContext
    | ClassFieldDecoratorContext
    ;

export function serialize(_target: any, context: Context): void {
    if (context.static || context.private) {
        throw new Error("Can only serialize public instance members.")
    }
    if (typeof context.name === "symbol") {
        throw new Error("Cannot serialize symbol-named properties.");
    }

    const propNames =
        (context.metadata[serializables] as string[] | undefined) ??= [];
    propNames.push(context.name);
}

export function jsonify(instance: object): string {
    const metadata = instance.constructor[Symbol.metadata];
    const propNames = metadata?.[serializables] as string[] | undefined;
    if (!propNames) {
        throw new Error("No members marked with @serialize.");
    }

    const pairStrings = propNames.map(key => {
        const strKey = JSON.stringify(key);
        const strValue = JSON.stringify((instance as any)[key]);
        return `${strKey}: ${strValue}`;
    });

    return `{ ${pairStrings.join(", ")} }`;
}

這個模塊使用了一個稱為 serializables 的局部 symbol,用于存儲和檢索被標記為 @serializable 的屬性名稱。它在每次調用 @serializable 時將這些屬性名稱存儲在元數據上。當調用 jsonify 函數時,會從元數據中獲取屬性列表,并使用這些列表從實例中檢索實際的屬性值,最終對這些名稱和值進行序列化。

使用符號(Symbol)確實可以提供一定程度的私密性,因為它們不容易被外部訪問到。然而,如果其他人知道了符號的存在,并且能夠獲取到對象的原型,他們仍然可以通過符號來訪問和修改元數據。因此,在這種情況下,符號并不能完全保證數據的私密性。

作為替代方案,可以使用 WeakMap 來存儲元數據。WeakMap 是一種特殊的 Map 數據結構,它的鍵只能是對象,并且不會阻止對象被垃圾回收。在這種情況下,我們可以使用每個對象的原型作為鍵,將元數據存儲在對應的 WeakMap 實例中。這樣,只有持有 WeakMap 實例的代碼才能夠訪問和操作元數據,確保了數據的私密性,也減少了在代碼中進行類型斷言的次數。

const serializables = new WeakMap<object, string[]>();

type Context =
    | ClassAccessorDecoratorContext
    | ClassGetterDecoratorContext
    | ClassFieldDecoratorContext
    ;

export function serialize(_target: any, context: Context): void {
    if (context.static || context.private) {
        throw new Error("Can only serialize public instance members.")
    }
    if (typeof context.name !== "string") {
        throw new Error("Can only serialize string properties.");
    }

    let propNames = serializables.get(context.metadata);
    if (propNames === undefined) {
        serializables.set(context.metadata, propNames = []);
    }
    propNames.push(context.name);
}

export function jsonify(instance: object): string {
    const metadata = instance.constructor[Symbol.metadata];
    const propNames = metadata && serializables.get(metadata);
    if (!propNames) {
        throw new Error("No members marked with @serialize.");
    }
    const pairStrings = propNames.map(key => {
        const strKey = JSON.stringify(key);
        const strValue = JSON.stringify((instance as any)[key]);
        return `${strKey}: ${strValue}`;
    });

    return `{ ${pairStrings.join(", ")} }`;
}

由于這個特性還比較新,大多數運行環境尚未對其提供原生支持。如果要使用它,需要為 Symbol.metadata 添加一個 polyfill。下面這個簡單的示例應該適用于大多數情況:

Symbol.metadata ??= Symbol("Symbol.metadata");

此外,還需要將編譯目標設置為 es2022 或更低,并在 lib 設置中配置 "esnext" 或 "esnext.disposable"。

{
    "compilerOptions": {
        "target": "es2022",
        "lib": ["es2022", "esnext.decorators", "dom"]
    }
}

命名和匿名元組元素

元組類型支持為每個元素提供可選的標記或名稱。

type Pair<T> = [first: T, second: T];

這些標記并不會改變對元組的操作能力,它們僅僅是為了增加可讀性和工具支持。

然而,在以前的 TypeScript 版本中,有一個規則是元組不能在標記和非標記元素之間混合使用。換句話說,要么所有的元素都不帶標記,要么所有的元素都需要帶標記。

// ? 沒有標記
type Pair1<T> = [T, T];

// ? 都有標記
type Pair2<T> = [first: T, second: T];

// ? 
type Pair3<T> = [first: T, T];
//                         ~
// Tuple members must all have names or all not have names.

對于剩余元素,這可能會變得有些麻煩,因為只能強制添加一個標簽,比如"rest"或"tail"。

type TwoOrMore_A<T> = [first: T, second: T, ...T[]];
//                                          ~~~~~~
// Tuple members must all have names or all not have names.

// ?
type TwoOrMore_B<T> = [first: T, second: T, rest: ...T[]];

這也意味著這個限制必須在類型系統內部進行強制實施,這意味著 TypeScript 將丟失標記。

正如之前提到的,為了確保在元組類型中所有元素要么都帶有標簽,要么都不帶標簽的規則,TypeScript 在類型系統內部進行了相應的限制。這意味著 TypeScript 在類型檢查過程中會忽略元組元素的標簽信息,對于類型系統而言,元組中的元素只被視為按照它們的順序排列的一組類型。

因此,盡管您在定義元組類型時可以使用標簽,但在類型檢查和類型推斷過程中,TypeScript 不會考慮這些標簽。只有元組元素的順序和類型才會被 TypeScript 確認和驗證。

這樣做是為了確保遵守 TypeScript 的語法規則并維持類型系統的一致性。盡管在類型定義中可能會丟失標簽信息,但這不會影響元組的使用和功能。

type HasLabels = [a: string, b: string];
type HasNoLabels = [number, number];
type Merged = [...HasNoLabels, ...HasLabels];
//   ^ [number, number, string, string]
//
//     'a' and 'b' were lost in 'Merged'

在 TypeScript 5.2 中,對元組標記的全有或全無限制已經被解除,可以更好地處理帶有標簽的元組和展開操作。現在,可以在定義元組時為每個元素指定一個標記,并且在展開操作中保留這些標記。

聯合類型數組方法調用

在之前的 TypeScript 版本中,數組聯合類型調用方法可能會導致一些問題。

declare let array: string[] | number[];

array.filter(x => !!x);
//    ~~~~~~ error!

報錯如下:

此表達式不可調用。
  聯合類型 "{ <S extends string>(predicate: (value: string, index: number, array: string[]) => value is S, thisArg?: any): S[]; (predicate: (value: string, index: number, array: string[]) => unknown, thisArg?: any): string[]; } | { ...; }" 的每個成員都有簽名,但這些簽名都不能互相兼容。ts(2349)

在 TypeScript 5.2 中,對于數組聯合類型的方法調用進行了特殊處理。在之前的版本中,TypeScript 會嘗試確定每個數組類型的方法是否在整個聯合類型上都兼容。然而,由于缺乏一種一致的策略,TypeScript 在這些情況下往往束手無策。

在 TypeScript 5.2 中,對于數組的聯合類型,會采取一種特殊的處理方式。首先,根據每個成員的元素類型構造一個新的數組類型,然后在該類型上調用方法。

以上面的例子為例,string[] | number[] 被轉換為 (string | number)[](或者 Array<string | number>),然后在該類型上調用 filter 方法。需要注意的是,filter 方法將產生一個 Array<string | number> 而不是 string[] | number[],但是針對一個全新生成的值,出錯的風險較小。

這意味著在 TypeScript 5.2 中,許多像 filter、find、some、every 和 reduce 這樣的方法都可以在數組聯合類型上調用,而在之前的版本中則無法實現。

對象成員的逗號自動補全

在向對象添加新屬性時,很容易忘記添加逗號。以前,如果忘記了逗號并請求自動補全,TypeScript 會給出與此無關的糟糕的補全結果,容易令人困惑。

在 TypeScript 5.2 中,當忘記逗號時,它會優雅地提供對象成員的補全建議。為了避免直接拋出語法錯誤,它還會自動插入缺失的逗號。

內聯變量重構

TypeScript 5.2 現在提供了一種重構功能,可以將一個變量的內容復制并內聯到該變量在代碼中的所有使用位置。

通常情況下,如果要替換一個變量的使用點,我們需要手動復制變量的內容并替換每個使用點。而通過這個重構功能,TypeScript 可以自動完成這個過程,將變量的值內聯到其所有使用位置,從而簡化了代碼的修改過程。

"內聯變量"重構操作會將變量的值直接替換到變量的所有使用位置,從而消除了中間的變量。然而,這種操作可能會改變代碼的執行順序和邏輯,因為原本是通過變量來保存并復用值的地方,現在變成了每次都重新計算并使用初始化器的值。

這可能會導致一些意想不到的問題,特別是如果初始化器有副作用(例如修改其他變量或調用函數)時。因此,使用"內聯變量"重構時需要謹慎,并確保了解代碼中可能發生的變化和影響。

重大變更和正確性修復

lib.d.ts 更改

為 DOM 生成的類型可能對代碼庫產生影響。要了解更多信息,請參閱 TypeScript 5.2 中的 DOM 更新。

#labeledElementDeclarations 可能包含未定義的元素

為了支持混合使用帶標簽和未帶標簽的元素,TypeScript 的 API 發生了細微的變化。 TupleType 的 labelsElementDeclarations 屬性可能在元素未標記的每個位置保持未定義。

interface TupleType {
-     labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration)[];
+     labeledElementDeclarations?: readonly (NamedTupleMember | ParameterDeclaration | undefined)[];
  }

#module 和 #moduleResolution 必須在最近的 Node.js 設置下匹配

-module 和 --moduleResolution 選項均支持 node16 和 nodenext 設置。 這些實際上是“現代 Node.js”設置,應該在任何最近的 Node.js 項目中使用。 當這兩個選項在是否使用 Node.js 相關設置方面不一致時,項目實際上會配置錯誤。

在 TypeScript 5.2 中,當對 --module 和 --moduleResolution 選項之一使用 node16 或 nodenext 時,TypeScript 現在要求另一個具有類似的 Node.js 相關設置。 如果設置不同,可能會收到類似以下錯誤消息。

Option 'moduleResolution' must be set to 'NodeNext' (or left unspecified) when option 'module' is set to 'NodeNext'.

或者:

Option 'module' must be set to 'Node16' when option 'moduleResolution' is set to 'Node16'.

因此,例如 --module esnext --moduleResolution node16 將被拒絕,但最好單獨使用 --module nodenext 或 --module esnext --moduleResolution bundler。

合并符號的一致導出檢查

當兩個聲明合并時,它們必須在是否都導出的問題上達成一致。由于一個錯誤,TypeScript 在環境語境中,例如聲明文件或 declare module 塊中,錯過了特定的情況。例如,在下面的示例中,如果 replaceInFile 一次被聲明為導出函數,另一次被聲明為未導出的命名空間,TypeScript 將不會發出錯誤。

declare module 'replace-in-file' {
    export function replaceInFile(config: unknown): Promise<unknown[]>;
    export {};

    namespace replaceInFile {
        export function sync(config: unknown): unknown[];
  }
}

在環境模塊中,添加export { ... }或類似的導出語法,比如export default ...,會隱式改變所有聲明是否自動導出的行為。TypeScript 現在更一致地識別這些令人困惑的語義,并且會在所有的replaceInFile聲明上發出錯誤,要求它們的修飾符必須保持一致。將會出現以下錯誤提示:

Individual declarations in merged declaration 'replaceInFile' must be all exported or all local.

參考:https://devblogs.microsoft.com/typescript/announcing-typescript-5-2-rc/。

責任編輯:姜華 來源: 前端充電寶
相關推薦

2013-12-21 19:58:32

SDN應用資源管理平臺SDN

2009-06-24 14:18:47

資源管理敏捷項目

2021-11-22 16:21:28

Kubernetes 運維開源

2023-12-15 15:14:10

yarn任務管理資源管理

2013-12-03 18:31:43

SDN應用編排資源管理

2011-07-28 16:06:34

IOS 應用程序 資源

2021-07-19 09:47:37

阿里云Flink 1.12資源管理

2010-01-28 14:54:01

C++資源管理

2011-02-22 14:47:52

SQL Server資

2009-10-15 08:57:45

Windows 7電源管理

2009-03-24 09:05:54

資源管理IT管理廣通信達

2011-02-28 08:57:10

SQL Server資內存性能調優

2022-10-12 09:21:44

微軟Windows 11

2020-03-02 16:47:42

物聯網人力資源管理IOT

2024-04-26 00:03:00

機器學習人力資源管理

2018-07-17 15:05:34

京東Hadoop集群分布式資源管理

2012-11-27 10:53:24

CloudPhysicvSphere

2010-02-05 13:52:04

C++資源管理

2012-03-27 22:22:51

iMC基礎IT資源管理

2025-04-29 10:00:00

Kubernete云原生Helm
點贊
收藏

51CTO技術棧公眾號

欧美日韩电影在线观看| 在线欧美日韩国产| 国产日韩久久| 男人天堂av在线播放| 欧美伦理在线视频| 91.com视频| 日韩国产一级片| www视频在线观看免费| 国产精品综合久久| 国产成人+综合亚洲+天堂| 午夜精品久久久久99蜜桃最新版| 只有精品亚洲| 五月婷婷综合在线| 在线观看一区二区三区三州| 日日躁夜夜躁白天躁晚上躁91| 久久久久国产精品午夜一区| 久久成人精品电影| 99久久人妻无码精品系列| 秋霞影院一区| 在线日韩电影| 777奇米四色成人影色区| 欧美乱大交xxxxx潮喷l头像| 一级毛片视频在线| 成人免费精品视频| 国产在线精品播放| 欧美 日韩 精品| 综合久久十次| 中文字幕欧美日韩在线| www.日本高清| av人人综合网| 亚洲视频在线一区观看| 欧美一区二区三区四区五区六区 | 在线视频二区| 99久久婷婷国产综合精品电影| 91精品在线一区| 亚洲婷婷久久综合| 久久xxxx精品视频| 91极品女神在线| 欧美xxxx黑人xyx性爽| 精品中文字幕一区二区三区| 一本到一区二区三区| 欧美久久在线观看| www在线免费观看视频| 中文字幕精品一区二区精品绿巨人 | 久久精品国产久精国产一老狼| 毛片网站免费观看| 日韩三级视频| 日韩av在线不卡| 又黄又爽的网站| 草草视频在线一区二区| 日韩一级片网址| 欧美成人乱码一二三四区免费| 日本在线精品| 在线看不卡av| 污污的网站18| 国产成人77亚洲精品www| 日本一区二区不卡视频| 欧美第一黄网| 青草久久伊人| 国产亚洲成年网址在线观看| 欧美日韩精品一区| 黄色电影免费在线看| 久久综合一区二区| 欧美一区二区影视| 在线日本视频| 亚洲欧美日韩在线播放| 26uuu成人| 91精选在线| 亚洲一区欧美一区| 加勒比成人在线| 欧美13videosex性极品| 色哟哟在线观看一区二区三区| 精品久久久久av| av在线播放一区| 制服丝袜中文字幕一区| 国产精品19p| 精品在线网站观看| 欧美三级中文字| 男女男精品视频站| 伊人久久一区| 亚洲白拍色综合图区| 女人另类性混交zo| 成人国产一区二区三区精品麻豆| 欧美日韩国产免费一区二区| 国内精品国产三级国产aⅴ久| 视频一区中文字幕精品| 亚洲国产精品人久久电影| 一区二区视频观看| 欧美肉体xxxx裸体137大胆| 日韩视频亚洲视频| 国产一级二级三级视频| 久久人人97超碰国产公开结果| 日本久久91av| 国产裸体无遮挡| 白白色 亚洲乱淫| 色大师av一区二区三区| 午夜影院免费视频| 国产精品白丝jk黑袜喷水| 国产精品国产精品| 超碰在线影院| 亚洲国产综合色| www.欧美日本| 中文字幕一区二区三区日韩精品| 亚洲女同精品视频| 亚洲国产成人精品综合99| 免费久久99精品国产自在现线| 久久久噜噜噜久久中文字免| 三级网站在线播放| 国产成人免费在线观看不卡| 欧美日韩电影一区二区三区| 超鹏97在线| 91国偷自产一区二区开放时间| 中文字幕在线视频一区二区| 免费观看久久av| 久久久久久久电影一区| 一本到在线视频| 99re这里只有精品视频首页| 色哟哟免费网站| 伊人免费在线| 精品国产91久久久| aaaaaaaa毛片| 日韩av密桃| 欧美在线影院在线视频| 国产成人精品777777| 国产一区二区电影| 丝袜足脚交91精品| 日本三级一区| 精品久久久久香蕉网| 99热99这里只有精品| 视频在线观看一区二区三区| 国产在线欧美日韩| 精品一性一色一乱农村| 91麻豆精品国产91久久久久久| 非洲一级黄色片| 亚洲一区二区伦理| 国产日韩精品久久| av老司机在线观看| 亚洲国产福利在线| 久久亚洲精品大全| 国产高清不卡二三区| 中文字幕黄色大片| 亚洲热av色在线播放| 中文综合在线观看| 中文字幕在线观看你懂的| 国产亚洲午夜高清国产拍精品| 亚洲美免无码中文字幕在线| 黄瓜视频成人app免费| 亚洲精品国产精品自产a区红杏吧| 日韩女优一区二区| 国产精品88888| 日韩video| 日本超碰一区二区| 欧美高清无遮挡| 亚洲国产精品suv| 亚洲曰韩产成在线| 国产一级伦理片| 亚洲精品系列| 麻豆久久久9性大片| 不卡av播放| 亚洲免费一级电影| 无码视频在线观看| 国产精品污网站| 中文字幕有码av| 婷婷另类小说| 波多野结衣成人在线| 男女在线观看视频| 亚洲国产欧美自拍| 欧美日韩综合一区二区三区| 国产三区在线成人av| 99视频在线视频| 国产精品丝袜在线播放| 国模吧一区二区三区| 天堂在线中文字幕| 懂色av影视一区二区三区| 尤物视频最新网址| 久久97超碰色| av 日韩 人妻 黑人 综合 无码| а√中文在线天堂精品| 青草青草久热精品视频在线网站| 国产午夜精品一区理论片| 欧美二区乱c少妇| 欧美久久久久久久久久久久| 99久久99久久免费精品蜜臀| 人人爽人人av| 中文精品久久| 久久久久久99| 日韩av懂色| 国内精品久久久久久| 黄色视屏网站在线免费观看| 欧美一区二区三区爱爱| 午夜影院在线看| 粉嫩一区二区三区性色av| 欧美亚洲精品一区二区| 日韩毛片视频| 国产精品一区视频| 国产精品毛片久久久久久久久久99999999 | 欧美一区二区影院| 欧美成年黄网站色视频| 亚洲福利视频在线| 午夜视频网站在线观看| 中文字幕第一区二区| 在线观看你懂的视频| 久久久久久久欧美精品| 超薄肉色丝袜足j调教99| 亚洲妇女av| dy888夜精品国产专区| 欧美一区久久久| 久久久久国产精品免费网站| 电影av在线| 亚洲国产精彩中文乱码av| 伊人网免费视频| 精品国产精品自拍| 97精品在线播放| 久久先锋影音av鲁色资源网| 成人在线短视频| 久久蜜桃资源一区二区老牛| 分分操这里只有精品| 久久综合av| 日本亚洲导航| 亚洲人成网亚洲欧洲无码| 亚洲伊人成综合成人网| www.久久| 国产成人精品一区二区| www.综合网.com| 久久亚洲欧美日韩精品专区| 国产原创av在线| 亚洲精品99999| 亚洲精品成人电影| 欧美二区在线观看| 中文字幕永久免费视频| 日韩欧美国产免费播放| 国产乡下妇女做爰毛片| 亚洲激情一二三区| 四虎884aa成人精品| 精品一区二区在线观看| 成人免费xxxxx在线视频| 99精品国产在热久久婷婷| 日韩精品第1页| 国产精品精品| 一区二区日本伦理| 欧美在线电影| 亚洲精品二区| 日韩精品久久| 婷婷四房综合激情五月| 久久av影视| 美女被啪啪一区二区| 美女网站色精品尤物极品姐弟| 91免费版网站在线观看| 电影一区二区三区久久免费观看| 国产欧美精品日韩精品| 日本综合视频| 国产精品国产三级国产专播精品人 | 国产精品日日摸夜夜摸av| 久久精品国产亚洲AV熟女| 97精品国产露脸对白| 亚洲一区二区三区四区五区六区| 99久久99精品久久久久久 | 国产欧美一二三区| 黄色片网站免费| 亚洲国产精品ⅴa在线观看| 成都免费高清电影| 国产精品午夜在线| 国产成人免费在线观看视频| 国产精品福利av| 国产日产精品一区二区三区的介绍| 亚洲欧美在线高清| 日韩黄色免费观看| 亚洲成人一区在线| 国产午夜性春猛交ⅹxxx| 中文字幕欧美三区| 长河落日免费高清观看| 成人性生交大片免费看视频在线| 少妇极品熟妇人妻无码| 成人av综合一区| 草草影院第一页| 亚洲国产精品成人综合| 超碰手机在线观看| 亚洲高清视频在线| 亚洲大片免费观看| 欧美裸体一区二区三区| 亚洲av无码一区二区乱子伦| 亚洲精品电影网在线观看| 日本福利在线观看| 日日噜噜噜夜夜爽亚洲精品| 久久久久黄久久免费漫画| 日本91av在线播放| 日韩免费在线电影| 国产福利久久| 成人全视频免费观看在线看| 91久久久久久久一区二区| 999久久久久久久久6666| 麻豆精品传媒视频| 911精品美国片911久久久| 日韩欧美亚洲区| 亚洲国产日韩欧美在线| 亚洲 欧美 日韩 国产综合 在线| 日韩二区三区四区| www.欧美com| 国产日产亚洲精品系列| 黄色一级视频免费观看| 色噜噜狠狠一区二区三区果冻| 国产精品久久久久久免费免熟| 欧美mv日韩mv亚洲| 高清国产福利在线观看| 久久久久久久久久久网站| 国产成人免费| 国产一区精品视频| 91精品一区二区三区综合| 无码精品国产一区二区三区免费| 国产美女娇喘av呻吟久久| 少妇真人直播免费视频| 一区二区三区日韩在线观看| 黄色免费av网站| 精品粉嫩超白一线天av| 日本中文字幕在线播放| 69国产精品成人在线播放| 久久久久久久久成人| 四虎永久国产精品| 一区二区三区成人精品| 久久人人爽人人片| 亚洲私人黄色宅男| 亚洲一区二区视频在线播放| 亚洲免费小视频| 日韩电影免费看| 国产精品18毛片一区二区| 99热国内精品永久免费观看| 黄色片久久久久| 91麻豆视频网站| 国产五月天婷婷| 日韩色在线观看| 91精品久久久久久粉嫩| 91久久精品国产| 99精品视频精品精品视频| 波多野结衣作品集| 99v久久综合狠狠综合久久| 国产香蕉在线视频| 欧美一区二区三级| 69xxx在线| 999视频在线观看| 五月开心六月丁香综合色啪 | 久久免费看毛片| 日本女人一区二区三区| 亚洲AV无码片久久精品| 精品欧美激情精品一区| 人人妻人人澡人人爽久久av| 欧美激情亚洲国产| jizzjizzjizz欧美| 人妻互换免费中文字幕| 国产精品一二三在| 久草免费新视频| 日韩女优电影在线观看| 欧美性video| 国产精品久久久久久久久久久久午夜片 | 亚洲缚视频在线观看| 欧美日韩经典丝袜| 国产91色在线|亚洲| 伊人久久成人| 国产一级伦理片| 日韩欧美a级成人黄色| 国产黄在线观看免费观看不卡| 国产精品白嫩美女在线观看| 国产欧美日韩免费观看 | 国产成人精品亚洲日本在线观看| 欧美一区1区三区3区公司| 秋霞成人午夜伦在线观看| 青青青视频在线免费观看| 91精品免费在线观看| 日本在线观看大片免费视频| 国产一级二级三级精品| 免费中文字幕日韩欧美| 精品熟妇无码av免费久久| 亚洲丝袜美腿综合| 性欧美一区二区三区| 国内伊人久久久久久网站视频| 台湾佬综合网| 国产九九在线观看| 一卡二卡欧美日韩| 性xxxx视频| 国产精品久久久久久影视| 亚洲情侣在线| 国产不卡一二三| 91久久精品网| 污污视频在线| 欧美人与性禽动交精品| 久久97超碰色| 成人免费区一区二区三区| 中文字幕欧美亚洲| 日韩一二三区| 日本va中文字幕| 一区二区三区在线看| 欧美日韩伦理片| 91日本在线视频| 亚洲一区成人| 欧美三级日本三级| 国产亚洲精品一区二区| 欧美影院在线| 9久久婷婷国产综合精品性色| 亚洲综合网站在线观看| 成人在线免费视频| 97中文在线|