Typescript interface default values

前端 未结 10 1952
借酒劲吻你
借酒劲吻你 2020-12-02 14:44

I have the following interface in TypeScript:

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

I declare a variable of that t

相关标签:
10条回答
  • 2020-12-02 15:20

    Can I tell the interface to default the properties I don't supply to null? What would let me do this

    No. But by default they are undefined which is mostly just fine. You can use the following pattern, i.e have a type assertion at the point of creation:

    let x: IX = {} as any;
    
    x.a = 'xyz'
    x.b = 123
    x.c = new AnotherType()
    

    I have this and other patterns documented here : https://basarat.gitbook.io/typescript/main-1/lazyobjectliteralinitialization

    0 讨论(0)
  • 2020-12-02 15:21

    You can use the Partial mapped type as explained in the documentation: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html

    In your example, you'll have:

    interface IX {
        a: string;
        b: any;
        c: AnotherType;
    }
    
    let x: Partial<IX> = {
        a: 'abc'
    }
    
    0 讨论(0)
  • 2020-12-02 15:22

    You could use two separate configs. One as the input with optional properties (that will have default values), and another with only the required properties. This can be made convenient with & and Required:

    interface DefaultedFuncConfig {
      b?: boolean;
    }
    
    interface MandatoryFuncConfig {
      a: boolean;
    }
    
    export type FuncConfig = MandatoryFuncConfig & DefaultedFuncConfig;
     
    export const func = (config: FuncConfig): Required<FuncConfig> => ({
      b: true,
      ...config
    });
    
    // will compile
    func({ a: true });
    func({ a: true, b: true });
    
    // will error
    func({ b: true });
    func({});
    
    0 讨论(0)
  • 2020-12-02 15:31

    You can implement the interface with a class, then you can deal with initializing the members in the constructor:

    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];
            }
        }
    }
    

    Another approach is to use a factory function:

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

    Then you can simply:

    var ix: IX = null;
    ...
    
    ix = new IXClass(...);
    // or
    ix = ixFactory(...);
    
    0 讨论(0)
  • 2020-12-02 15:32

    I stumbled on this while looking for a better way than what I had arrived at. Having read the answers and trying them out I thought it was worth posting what I was doing as the other answers didn't feel as succinct for me. It was important for me to only have to write a short amount of code each time I set up a new interface. I settled on...

    Using a custom generic deepCopy function:

    deepCopy = <T extends {}>(input: any): T => {
      return JSON.parse(JSON.stringify(input));
    };
    

    Define your interface

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

    ... and define the defaults in a separate const.

    const XDef : IX = {
        a: '',
        b: null,
        c: null,
    };
    

    Then init like this:

    let x : IX = deepCopy(XDef);
    

    That's all that's needed..

    .. however ..

    If you want to custom initialise any root element you can modify the deepCopy function to accept custom default values. The function becomes:

    deepCopyAssign = <T extends {}>(input: any, rootOverwrites?: any): T => {
      return JSON.parse(JSON.stringify({ ...input, ...rootOverwrites }));
    };
    

    Which can then be called like this instead:

    let x : IX = deepCopyAssign(XDef, { a:'customInitValue' } );
    

    Any other preferred way of deep copy would work. If only a shallow copy is needed then Object.assign would suffice, forgoing the need for the utility deepCopy or deepCopyAssign function.

    let x : IX = object.assign({}, XDef, { a:'customInitValue' });
    

    Known Issues

    • It will not deep assign in this guise but it's not too difficult to modify deepCopyAssign to iterate and check types before assigning.
    • Functions and references will be lost by the parse/stringify process. I don't need those for my task and neither did the OP.
    • Custom init values are not hinted by the IDE or type checked when executed.
    0 讨论(0)
  • 2020-12-02 15:36

    Factory method:

    You could use a factory method for this which returns an object which implements the XI interface.

    Example:

    class AnotherType {}
    
    interface IX {
        a: string,
        b: any,
        c: AnotherType | null
    }
    
    function makeIX (): IX {
        return {
        a: 'abc',
        b: null,
        c: null
        }
    }
    
    const x = makeIX();
    
    x.a = 'xyz';
    x.b = 123;
    x.c = new AnotherType();
    

    The only thing I changed with regard to your example is made the property c both AnotherType | null. Which will be necessary to not have any compiler errors (This error was also present in your example were you initialized null to property c).

    0 讨论(0)
提交回复
热议问题