ค้นหาเว็บไซต์

tsc - TypeScript ไม่สนใจประเภทของ const ทำไมสิ่งนี้ถึงคอมไพล์ได้?


ในบางกรณีฉันกำหนดชนิดของ const และ TypeScript มองข้ามมัน โดยเฉพาะบางครั้งฉันเพิ่ม | undefined และ TypeScript มองข้ามความจริงว่าค่าดังกล่าวอาจเป็น undefined ฉันกำลังพลาดอะไรบางอย่างหรือเปล่า?

ฉันกำลังดึงองค์ประกอบจาก Array หรือ NodeList ฉันบันทึกมันใน let หรือ const ตามค่าเริ่มต้น TypeScript สมมติว่าการเข้าถึงอาเรย์จะไม่ล้มเหลว ฉันรู้ว่าการเข้าถึงอาเรย์นี้โดยเฉพาะมีแนวโน้มที่จะล้มเหลว ฉันกำลังพยายามกำหนดชนิดของนิพจน์ เพื่อว่าเมื่อฉันพยายามใช้งานต่อไป ฉันจะไม่ลืมตรวจสอบ undefined นี่คือตัวอย่าง:

const myArray = ["a", "b", "see", "d", "e", "f"];

// Default:
export const plain0 = myArray[0];    // "a"
export const plain9 = myArray[9];    // undefined
export const plainMissing = undefined; // undefined

console.log(plain0.length);  // This will work as expected.
console.log(plain9.length);  // This will cause a runtime error.
console.log(plainMissing.length);  // Does not compile.

// With explicitly variable declarations:
export const variableType0 : string | undefined = myArray[0];
export const variableType9 : string | undefined = myArray[9];
export const variableTypeMissing : string | undefined = undefined; // undefined

console.log(variableType0.length);  // Compiles but shouldn't!!!!
console.log(variableType9.length);  // Compiles but shouldn't!!!!
console.log(variableTypeMissing.length);  // Does not compile!

VS Code และ tsc เห็นตรงกันในผลลัพธ์ พวกมันทั้งสองละเว้นการประกาศประเภทของฉันและอนุญาตให้ฉันเข้าถึง string | undefined ราวกับว่ามันเป็น string ตัวคอมไพเลอร์กำลังละเว้นการประกาศของฉัน มันกำลังปฏิบัติต่อ variableType* ในโค้ดของฉันเหมือนกับ plain*

ฉันขอให้ tsc สร้างไฟล์ .d.ts ให้ฉัน ซึ่งยืนยันว่ามันรู้ว่าชุดค่าคงที่ชุดที่สองควรมีประเภทต่างจากชุดแรก

export declare const plain0: string;
export declare const plain9: string;
export declare const plainMissing: undefined;
export declare const variableType0: string | undefined;
export declare const variableType9: string | undefined;
export declare const variableTypeMissing: string | undefined;

เมื่อฉันอยู่ใน VS Code และฉันวางตัวชี้เมาส์เหนือคำสั่ง const ใด ๆ เครื่องมือคำแนะนำจะแสดงประเภทเดียวกันกับไฟล์ .d.ts

แต่เมื่อฉันวางเมาส์เหนือคำสั่ง log ใด ๆ ฉันเห็นชนิดข้อมูลที่แตกต่างกัน ในบรรทัดเหล่านั้น เครื่องมือแสดงคำแนะนำสำหรับ variableType0, variableType9 และ variableTypeMissing คือ string, string, และ undefined ตามลำดับ ชนิดข้อมูลเหล่านั้นตรงกับข้อผิดพลาดที่ฉันเห็นอย่างสมบูรณ์ แต่ชนิดเหล่านั้น ไม่ตรงกับการประกาศของพวกมัน

มีเหตุผลบางอย่างหรือเปล่าที่ TypeScript ไม่สนใจการประกาศของฉัน? ฉันทำอะไรผิดไปหรือเปล่า? ฉันเข้าใจกฎผิดหรือไม่? นี่เป็นบั๊กในคอมไพเลอร์หรือเปล่า? ฉันจะทำเครื่องหมายโค้ดอย่างไรเพื่อให้คอมไพเลอร์บังคับให้ฉันใช้ ?. ไม่ใช่แค่ .?

นี่คือไฟล์ tsconfig.json ของฉัน ส่วนใหญ่ได้มาจาก vite แต่ฉันเปลี่ยนบางอย่างไปบ้าง

{
  "compilerOptions": {
    "declaration": true,
    "declarationDir": "./types",
    "noImplicitAny": true,
    "target": "ESNext",
    "useDefineForClassFields": true,
    "module": "ESNext",
    "lib": ["ESNext", "DOM"],
    "moduleResolution": "Node",
    "strict": true,
    "sourceMap": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "emitDeclarationOnly": true,
    "noUnusedLocals": false,
    "noUnusedParameters": false,
    "noImplicitReturns": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}

แก้ไข/ตัวอย่างที่ง่ายกว่า

const x : number|undefined = Math.random();
x.toFixed();

const y : number|string = Math.random();
y.toFixed();

เวอร์ชันนี้ไม่ใช้ arrays ผมกำลังเจอกับปัญหาเดียวกัน TypeScript คิดว่ามันรู้ประเภทของตัวแปรอยู่แล้ว ผมกำลังบอก TypeScript ว่าผมรู้อย่างดีกว่าด้วยการให้การประกาศ TypeScript ไม่ให้ข้อผิดพลาดกับผม โดยบอกว่ามันไม่ชอบการประกาศของผม แต่แล้วมันก็ละเลยการประกาศของผม

คำตอบ #1

การพูดถึงตัวอย่างที่เรียบง่ายของคุณ:

const x : number|undefined = Math.random();
x.toFixed();

TypeScript บางครั้งสามารถจำกัดประเภทที่ปรากฏของตัวแปร/คุณสมบัติ/นิพจน์ได้

โดยเฉพาะอย่างยิ่ง เมื่อค่าตัวแปรเป็นชนิด union (เช่น number | undefined) ตัวคอมไพเลอร์จะลดชนิดที่ปรากฏของตัวแปรนั้นเมื่อมีการ กำหนดค่า ให้เหลือเฉพาะสมาชิก union ที่เข้ากับค่าที่ถูกกำหนด เนื่องจาก Math.random() เป็นชนิด number การกำหนดค่า x=Math.random() จะลดชนิดที่ปรากฏของ x จาก number | undefined เป็น number

นี่โดยทั่วไปถือว่าเป็นพฤติกรรมที่ต้องการ; หากไม่มีการจำกัดประเภท คุณจะไม่สามารถทำการการ์ดประเภทค่า; การตรวจสอบเช่น if (x !== undefined) x.toFixed() จะไม่ทำงานเพราะการตรวจสอบ x !== undefined จะไม่มีผลต่อประเภทที่ปรากฏของ x หากไม่มีการจำกัดประเภทจากการกำหนดค่า, if (x !== undefined) x=5; ตามด้วย x.toFixed() จะเกิดข้อผิดพลาด

ในทุกกรณี หากคุณไม่ชอบพฤติกรรมนี้ วิธีเดียวที่จะแก้ไขคือการตรวจสอบให้แน่ใจว่าค่าที่ถูกกำหนดนั้นขยายเป็นประเภทยูเนียนทั้งหมดก่อนการกำหนด วิธีที่ง่ายที่สุดในการทำเช่นนี้คือการใช้การยืนยันประเภทกับค่านี้ นิพจน์ Math.random() as number | undefined เป็นประเภท number | undefined ดังนั้นถ้าคุณกำหนดค่านั้นให้กับ x คุณจะไม่เกิดการจำกัดประเภท:

const y = Math.random() as number | undefined;
y.toFixed(); // error

ลิงก์ Playground ไปยังโค้ด

คำตอบ #2

ฉันพบทางแก้ปัญหาแล้ว

export const expressionType0 = myArray[0] as string | undefined;
export const expressionType9 = myArray[9] as string | undefined;

console.log(expressionType0.length);  // Does not compile!
console.log(expressionType9.length);  // Does not compile!

นี่ทำในสิ่งที่ฉันต้องการ มันสั่งให้ TypeScript เพิกเฉยกฎปกติของมัน เพียงในสองกรณีนี้เท่านั้นที่ต้องตรวจสอบดัชนีของอาเรย์ VS Code ยังแนะนำให้ฉันแทนที่ . ด้วย ?.

ฉันยังคงอยากเข้าใจว่าทำไมความพยายามครั้งแรกของฉันถึงล้มเหลว การแปลงนิพจน์ทำงานได้ตรงตามที่ฉันต้องการ แต่การแปลงตัวแปรไม่ได้