Typescript default value in interface

Typescript interface default values

But I don’t like having to specify a bunch of default null values for each property when declaring the object when they’re going to just be set later to real values. Can I tell the interface to default the properties I don’t supply to null? What would let me do this:

without getting a compiler error. Right now it tells me

> TS2322: Type ‘<>‘ is not assignable to type > ‘IX’. Property ‘b’ is missing in type ‘<>‘.

Typescript Solutions

Solution 1 — Typescript

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

No. You cannot provide default values for interfaces or type aliases as they are compile time only and default values need runtime support

Alternative

But values that are not specified default to undefined in JavaScript runtimes. So you can mark them as optional:

interface IX < a: string, b?: any, c?: AnotherType > 

And now when you create it you only need to provide a :

You can provide the values as needed:

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

Solution 2 — Typescript

You can’t set default values in an interface, but you can accomplish what you want to do by using Optional Properties (compare paragraph #3):

Simply change the interface to:

interface IX < a: string, b?: any, c?: AnotherType > 

And use your init function to assign default values to x.b and x.c if those properies are not set.

Solution 3 — Typescript

While @Timar’s answer works perfectly for null default values (what was asked for), here another easy solution which allows other default values: Define an option interface as well as an according constant containing the defaults; in the constructor use the spread operator to set the options member variable

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); > > 

Now you can use the class like this:

const x = new ClassX(< a: "set" >); x.printOptions(); 

Solution 4 — Typescript

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 > > 
var ix: IX = null; . ix = new IXClass(. ); // or ix = ixFactory(. ); 

Solution 5 — Typescript

In your example, you’ll have:

interface IX < a: string; b: any; c: AnotherType; > let x: Partial = < a: 'abc' > 

Solution 6 — Typescript

I use the following pattern:

Create utility type Defaults :

type OptionalKeysT> =  [K in keyof T]-?: > extends PickT, K> ? K : never >[keyof T]; type DefaultsT> = RequiredPickT, OptionalKeysT>>> 

Declare class with options/defaults:

// options passed to class constructor export interface Options < a: string, b?: any, c?: number > // defaults const defaults: DefaultsOptions> = < b: null, c: 1 >; export class MyClass < // all options in class must have values options: RequiredOptions>; 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 > 

Solution 7 — Typescript

Default values to an interface are not possible because interfaces only exists at compile time.

Alternative solution:

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).

Solution 8 — Typescript

It’s best practice in case you have many parameters to let the user insert only few parameters and not in specific order.

foo(a?, b=1, c=99, d=88, e?) foo(null, null, null, 3) 

Since you have to supply all the parameters before the one you actually want (d).

The way to do it is through interfaces. You need to define the parameter as an interface like:

interface Arguments < a?; b?; c?; d?; e?; > 

And define the function like:

Now interfaces variables can’t get default values, so how do we define default values?

Simple, we define default value for the whole interface:

foo(< a, b=1, c=99, d=88, e >: Arguments) 

The actual parameters will be:

Another option without declaring an interface is:

foo(< a=undefined, b=1, c=99, d=88, e=undefined >) 

Follow Up: In the previous function definition we define defaults for the fields of the parameter object, but not default for the object itself. Thus we will get an extraction error (e.g. Cannot read property ‘b’ of undefined ) from the following call:

There are two possible solutions:

const defaultObject = undefined, b=1, c=99, d=88, e=undefined> function foo( = defaultObject) 
const defaultObject = undefined, b=1, c=99, d=88, e=undefined> function foo(object) < const = < . defaultObject, . object, > //Continue the function code.. > 

Solution 9 — Typescript

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 = extends <>>(input: any): T => < return JSON.parse(JSON.stringify(input)); >; 
interface IX < a: string; b: any; c: AnotherType; > 

. and define the defaults in a separate const.

const XDef : IX = < a: '', b: null, c: null, >; 

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

deepCopyAssign = 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.

Solution 10 — Typescript

I have created wrapper over Object.assign to fix typing issues.

export function assignT>(. args: T[] | PartialT>[]): T  return Object.assign.apply(Object, [>, . args]); > 
export interface EnvironmentValues < export interface EnvironmentValues < isBrowser: boolean; apiURL: string; > export const enviromentBaseValues: PartialEnvironmentValues> = < isBrowser: typeof window !== 'undefined', >; export default enviromentBaseValues; 
import < EnvironmentValues, enviromentBaseValues > from './env.base'; import < assign >from '../utilities'; export const enviromentDevValues: EnvironmentValues = assignEnvironmentValues>( < apiURL: '/api', >, enviromentBaseValues ); export default enviromentDevValues; 

Solution 11 — Typescript

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 => (< b: true, . config >); // will compile func(< a: true >); func(< a: true, b: true >); // will error func(< b: true >); func(<>); 

Solution 12 — Typescript

It is depends on the case and the usage. Generally, in TypeScript there are no default values for interfaces.

If you don’t use the default values
You can declare x as:

let x: IX | undefined; // declaration: x = undefined 

Then, in your init function you can set real values:

x = < a: 'xyz' b: 123 c: new AnotherType() >; 

In this way, x can be undefined or defined — undefined represents that the object is uninitialized, without set the default values, if they are unnecessary. This is loggically better than define «garbage».

If you want to partially assign the object:
You can define the type with optional properties like:

interface IX < a: string, b?: any, c?: AnotherType > 

In this case you have to set only a . The other types are marked with ? which mean that they are optional and have undefined as default value.

Which makes all the fields optional.

In any case you can use undefined as a default value, it is just depends on your use case.

Solution 13 — Typescript

You can also have a helper method/function that returns the object with default property values, and then the calling code can override the defaults as needed. That’s the approach I’m following as I’ve been faced with the same question in my current project. This way coding the default property value object is a one time affair, and you can reuse this object throughout your entire application.

Solution 14 — Typescript

Another way is to use https://www.npmjs.com/package/merge
This is the same as the last answer but a bit tidier.

Next lets create an interface with some options.
We’ll place this into
./types/index.ts

export interface ExampleOpts < opt1: string, opt2: string, opt3: string, > 

Next lets create a set of defaults
you could put this in the same file, but lets keep the types seperate and place it into
./config/index.ts

import < ExampleOpts > from '../types' // Defaults export const ExampleOptsDefault : ExampleOpts = < opt1: 'default value 1', opt2: 'default value 2', opt3: 'default value 3', > 

Next lets join it all together with a function within
./index.ts

import < ExampleOpts > from './types' import < ExampleOptsDefault > from './config' import merge from 'merge' // The ? makes the parameter optional export function test1(options?: ExampleOpts) < // merge tries to load in the defaults first, then options next if it's defined const merged_opts: ExampleOpts = merge.recursive(ExampleOptsDefault, options) // log the result to the console console.log(merged_opts) > 

Solution 15 — Typescript

Another way is to use the Pick utility type and choose the properties you wish to set to required.

interface IX < a: string, b: any, c: AnotherType > let x: Pick'a'> = < a: 'abc' > 

Then when you want to declare the real IX object, you just merge the defaults with new values, like so:

Solution 16 — Typescript

Working through this now. Use a class instead of an interface .

class IX < a: String = ''; b?: any; c: Cee = new Cee(); > class Cee < c: String = 'c'; e: String = 'e'; > 

Источник

Читайте также:  Python file system paths
Оцените статью