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 ยังแนะนำให้ฉันแทนที่ . ด้วย ?.
ฉันยังคงอยากเข้าใจว่าทำไมความพยายามครั้งแรกของฉันถึงล้มเหลว การแปลงนิพจน์ทำงานได้ตรงตามที่ฉันต้องการ แต่การแปลงตัวแปรไม่ได้
