typescript - วิธีเขียนการรักษาประเภทที่ผู้ใช้กำหนดสำหรับ "string" | "literal" | "types"?
ฉันมีฟังก์ชันดังต่อไปนี้:
change(key: keyof McuParams, dispatch: Function) {
/** Body removed for brevity */
}
เมื่อฉันเรียกฟังก์ชัน
this.change(VARIABLE_FROM_MY_API, this.props.dispatch)
... ฉัน (ซึ่งก็เข้าใจได้) ได้รับข้อผิดพลาดดังต่อไปนี้:
Argument of type 'string' is not assignable to parameter of type '"foo" | "bar"'นี่มีเหตุผลเนื่องจากไม่มีทางที่ตัวคอมไพเลอร์จะรู้ได้ว่า API ของฉันกำลังส่งอะไรในเวลาคอมไพล์ อย่างไรก็ตาม การป้องกันประเภทที่ผู้ใช้กำหนดขึ้นบางครั้งสามารถใช้เพื่ออนุมานข้อมูลประเภทในเวลารันไทม์และส่งข้อมูลนั้นไปยังคอมไพเลอร์ผ่านเงื่อนไขได้
เป็นไปได้หรือไม่ที่จะเขียนตัวป้องกันประเภทที่กำหนดเองสำหรับประเภทสตริง keyof เช่น keyOf foo เมื่อ foo ถูกกำหนดเป็นเพียงประเภทเท่านั้น (ไม่ใช่ในอาร์เรย์)? ถ้าเป็นไปได้ ทำอย่างไร?
คำตอบ #1
นี่คือตัวอย่าง:
interface McuParams {
foo, bar, baz;
}
function change(key: keyof McuParams, dispatch: Function) {
}
function isKeyOfMcuParams(x: string): x is keyof McuParams {
switch (x) {
case 'foo':
case 'bar':
case 'baz':
return true;
default:
return false;
}
}
function doSomething() {
const VAR_FROM_API = <string>'qua';
if (!isKeyOfMcuParams(VAR_FROM_API)) return;
change(VAR_FROM_API, () => { });
}
ใน doSomething คุณสามารถใช้บล็อกการควบคุมการทำงานใดก็ได้ที่คุณต้องการแทน return (เช่น if หรือ throw เป็นต้น)
คำตอบ #2
ลองทำตามนี้:
enum mcuParams { foo, bar };
type McuParams = keyof typeof mcuParams;
function isMcuParams(value: string) : value is McuParams {
return mcuParams.hasOwnProperty(value);
}
function change(key: McuParams) {
//do something
}
let someString = 'something';
if (isMcuParams(someString)) {
change(someString);
}
อัปเดตล่าสุด :
ตัวอย่างที่ฉันเขียนไว้ข้างต้นสมมติว่าเรารู้ค่าที่เป็นไปได้ของ McuParams ('foo' หรือ 'bar') แล้ว ตัวอย่างด้านล่างนี้ไม่ได้สมมติอะไรเลย ฉันทดสอบแล้วและมันทำงานตามที่คาดไว้ ทุกครั้งที่คุณรันโค้ด คุณจะได้ผลลัพธ์ที่แตกต่างกันขึ้นอยู่กับค่าที่สร้างขึ้นแบบสุ่ม
function getAllowedKeys() {
//get keys from somewhere. here, I generated 2 random strings just for the sake of simplicity
let randomString1 = String(Math.round(Math.random())); //0 or 1
let randomString2 = String(Math.round(Math.random())); //0 or 1
return Promise.resolve([randomString1, randomString2]);
}
function getKeyToBeTested() {
//same as in 'getAllowedKeys'
let randomString = String(Math.round(Math.random())); //0 or 1
return Promise.resolve(randomString);
}
Promise.all([getAllowedKeys(), getKeyToBeTested()]).then((results) => {
let allowedKeys: string[] = results[0];
let keyTobeTested: string = results[1]; //'0' or '1'
let mcuParams = {};
for (let entry of results[0]) { //create dictionary dynamically
mcuParams[entry] = ''; //the value assigned is indiferent
}
//create type dynamically. in this example, it could be '0', '1' or '0' | '1'
type McuParams = keyof typeof mcuParams;
//create Type Guard based on allowedKeys fetched from somewhere
function isMcuParams(value:string) : value is McuParams {
return mcuParams.hasOwnProperty(value);
}
function change(key: McuParams) {
//do something
alert('change function executed: [' + allowedKeys.toString() + '] - ' + keyTobeTested);
}
if (isMcuParams(keyTobeTested)) {
change(keyTobeTested);
}
else {
alert('change function not executed: [' + allowedKeys.toString() + '] - ' + keyTobeTested);
}
});
