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

ค่าดีฟอลต์ของอินเตอร์เฟซใน Typescript


ฉันมีอินเตอร์เฟซดังต่อไปนี้ใน TypeScript:

interface IX {
    a: string,
    b: any,
    c: AnotherType
}

ฉันประกาศตัวแปรของประเภทนั้นและฉันกำหนดค่าเริ่มต้นให้กับคุณสมบัติทั้งหมด

let x: IX = {
    a: 'abc',
    b: null,
    c: null
}

แล้วฉันกำหนดค่าจริงให้กับมันในฟังก์ชัน init ภายหลัง

x.a = 'xyz'
x.b = 123
x.c = new AnotherType()

แต่ผมไม่ชอบที่จะต้องระบุค่ามาตรฐานเป็น null สำหรับแต่ละคุณสมบัติเมื่อประกาศอ็อบเจ็กต์ทั้งๆ ที่ค่าพวกนั้นจะถูกตั้งเป็นค่าจริงในภายหลัง ผมสามารถบอกอินเทอร์เฟซให้ตั้งค่าคุณสมบัติที่ผมไม่ได้ระบุเป็น null โดยอัตโนมัติได้ไหม? อะไรที่จะทำให้ผมทำแบบนี้ได้:

let x: IX = {
    a: 'abc'
}

โดยไม่เกิดข้อผิดพลาดของคอมไพเลอร์ ตอนนี้มันบอกฉันว่า

TS2322: ประเภท '{}' ไม่สามารถกำหนดค่าให้กับประเภท 'IX' ได้ คุณสมบัติ 'b' ไม่มีในประเภท '{}'

คำตอบ #1

ฉันสามารถบอกอินเทอร์เฟซให้ตั้งค่าคุณสมบัติที่ฉันไม่ได้ระบุเป็น null ได้ไหม? อะไรที่จะทำให้ฉันทำสิ่งนี้ได้

ไม่ คุณไม่สามารถให้ ค่าเริ่มต้น สำหรับอินเทอร์เฟซหรืออาลิแอสของชนิดได้เนื่องจากมันมีผลเฉพาะเวลาคอมไพล์ และค่าดีฟอลต์จำเป็นต้องมีการสนับสนุนเวลารันไทม์

ทางเลือก

แต่ค่าที่ไม่ได้ระบุจะถูกตั้งค่าเริ่มต้นเป็น undefined ในรันไทม์ของ JavaScript ดังนั้นคุณสามารถทำเครื่องหมายว่าเป็นค่าที่ไม่จำเป็นได้:

interface IX {
  a: string,
  b?: any,
  c?: AnotherType
}

และตอนนี้เมื่อคุณสร้างมัน คุณเพียงแค่ต้องระบุ a:

let x: IX = {
    a: 'abc'
};

คุณสามารถให้ค่าตามที่ต้องการได้:

x.a = 'xyz'
x.b = 123
x.c = new AnotherType()

คำตอบ #2

คุณไม่สามารถกำหนดค่าดีฟอลต์ในอินเทอร์เฟซได้ แต่คุณสามารถทำสิ่งที่คุณต้องการได้โดยการใช้คุณสมบัติที่เป็นอ็อปชัน:

เพียงแค่เปลี่ยนอินเทอร์เฟซเป็น:

interface IX {
    a: string,
    b?: any,
    c?: AnotherType
}

คุณสามารถทำได้ดังนี้:

let x: IX = {
    a: 'abc'
}

และใช้ฟังก์ชัน init ของคุณเพื่อกำหนดค่าดีฟอลต์ให้กับ x.b และ x.c หากคุณสมบัติเหล่านั้นยังไม่ได้ถูกตั้งค่า

คำตอบ #3

ในขณะที่คำตอบของ @Timar ใช้งานได้อย่างสมบูรณ์สำหรับค่าเริ่มต้น null (ซึ่งเป็นสิ่งที่ถูกถามมา) นี่คืออีกหนึ่งวิธีง่าย ๆ ที่อนุญาตให้ใช้ค่าเริ่มต้นอื่น ๆ ได้: กำหนดอินเทอร์เฟซตัวเลือกรวมถึงค่าคงที่ที่เกี่ยวข้องซึ่งมีค่าเริ่มต้น; ในตัวสร้าง ใช้ตัวดำเนินการกระจายเพื่อกำหนดตัวแปรสมาชิก options

interface IXOptions {
    a?: string,
    b?: any,
    c?: number
}

const XDefaults: IXOptions = {
    a: "default",
    b: null,
    c: 1
}

export class ClassX {
    private options: IXOptions;

    constructor(XOptions: IXOptions) {
        this.options = { ...XDefaults, ...XOptions };
    }

    public printOptions(): void {
        console.log(this.options.a);
        console.log(this.options.b);
        console.log(this.options.c);
    }
}

ตอนนี้คุณสามารถใช้คลาสได้แบบนี้:

const x = new ClassX({ a: "set" });
x.printOptions();

ผลลัพธ์ :

set
null
1

คำตอบ #4

คุณสามารถนำอินเทอร์เฟซไปใช้กับคลาสได้ จากนั้นคุณสามารถจัดการกับการเริ่มต้นสมาชิกในคอนสตรัคเตอร์ได้:

class IXClass implements IX {
    a: string;
    b: any;
    c: AnotherType;

    constructor(obj: IX);
    constructor(a: string, b: any, c: AnotherType);
    constructor() {
        if (arguments.length == 1) {
            this.a = arguments[0].a;
            this.b = arguments[0].b;
            this.c = arguments[0].c;
        } else {
            this.a = arguments[0];
            this.b = arguments[1];
            this.c = arguments[2];
        }
    }
}

วิธีอีกอย่างคือการใช้ฟังก์ชันโรงงาน:

function ixFactory(a: string, b: any, c: AnotherType): IX {
    return {
        a: a,
        b: b,
        c: c
    }
}

จากนั้นคุณสามารถทำได้ง่ายๆ:

var ix: IX = null;
...

ix = new IXClass(...);
// or
ix = ixFactory(...);

คำตอบ #5

คุณสามารถใช้ประเภทแมป Partial ตามที่อธิบายในเอกสาร: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html

ในตัวอย่างของคุณ คุณจะมี:

interface IX {
    a: string;
    b: any;
    c: AnotherType;
}

let x: Partial<IX> = {
    a: 'abc'
}

คำตอบ #6

ฉันใช้รูปแบบดังต่อไปนี้:

สร้างประเภทยูทิลิตี้ Defaults<T>:

type OptionalKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never }[keyof T];
type Defaults<T> = Required<Pick<T, OptionalKeys<T>>>

ประกาศคลาสพร้อมตัวเลือก/ค่าเริ่มต้น:

// options passed to class constructor
export interface Options {
    a: string,
    b?: any,
    c?: number
}

// defaults
const defaults: Defaults<Options> = {
    b: null,
    c: 1
};

export class MyClass {
    // all options in class must have values
    options: Required<Options>;

    constructor(options: Options) {
        // merge passed options and defaults
        this.options = Object.assign({}, defaults, options);
    }
}

สร้างอินสแตนซ์ของคลาส:

const myClass = new MyClass({
    a: 'hello',
    b: true,
});

console.log(myClass.options);
// { a: 'hello', b: true, c: 1 }

บทความที่เกี่ยวข้อง