Reputation: 58903
I want to pass an object as argument for the constructor of a class. Some keys of the options object are optional.
Is there a better / more idiomatic way of accomplishing the following in typescript? Thanks
class Car {
color: number;
numberOfWheels: number;
constructor (options: {color: number, numberOfWheels?: number}) {
options.numberOfWheels |= 4;
this.color = options.color;
this.numberOfWheels = options.numberOfWheels;
}
}
Upvotes: 13
Views: 12192
Reputation: 270
A bit late, and mostly just to build up on readability of the program, I am using the following pattern:
export interface Params {
color: number;
brand?: string;
numberOfWheels?: number;
withBreaks?: boolean; // wait, why wouldn't a car have breaks?
};
export function DefaultParams({
color,
brand = "Mercedes",
numberOfWheels = 4,
withBreaks = true
}: Params): Params {
return {color, brand, numberOfWheels, withBreaks};
};
To my perspective this is clearer, since it reads vertically in the same way (and order) it was declared, and also then when you actually use the params you can just write:
class Car {
params: Params; // or just each of the elements, but this way
// you don't care if you later add more params
constructor (p: Params) {
this.params = DefaultParams(p);
}
}
This leaves the intention very clear, and all the logic related to params and their initialization can be decoupled. A user of your code will know very well what are the defaults for each value, but they don't have to read all the destructuring thing if they don't care for it.
You can also perform simple validation that escapes the type system in DefaultParams
, like the following:
function DefaultParams({
color,
brand,
numberOfWheels = 4,
withBreaks = true
}: Params): Params {
if (brand=="Mercedes" && numberOfWheels==16){
throw new Error("sorry, this kind of big trucks are not yet supported");
}
return {color, brand, numberOfWheels, withBreaks};
};
In that case, you could pick a better name rather than DefaultParams
since you're not just returning default params but also validating. Something like ValidateParams
can work, since it gives the user the hint that you will be using this to validate what they pass, and the destructuring with defaults will also tell them what are you picking as defaults, so they wouldn't just set numberOfWheels
to 16 if they know the default brand is Mercedes
to prevent the validation error.
Upvotes: 2
Reputation: 152095
Here is an idiomatic way of defining default values for constructors in TypeScript, that scales better as the number of parameters increases.
The idea is to define an interface for the constructor parameters, then assign those parameters to a class field (this.params
), so they're available inside methods, grouped as one object. Then in the constructor, we use the spread syntax for object literals to assign the default values.
interface CarParams {
color: string;
engine: 'electric' | 'gasoline';
selfDriving?: boolean;
numberOfWheels?: number;
}
class Car {
readonly params: CarParams;
constructor (params: CarParams) {
this.params = {
// Initialize with the default parameters.
numberOfWheels: 4,
selfDriving: false,
// Overwrite corresponding defaults with any specified values.
...params
}
}
}
const tesla = new Car({
color: 'red',
engine: 'electric',
});
console.log(tesla.params);
// {
// "numberOfWheels": 4,
// "selfDriving": false,
// "color": "red",
// "engine": "electric"
// }
}
You can run the code in the TypeScript Playground.
This idiom avoids the repeated this.property = property
assignments seen in the other answers. You only need to repeat the names of the parameters for which you assign default values in the constructor.
I don't think it's possible to repeat the parameter names less than that and keep the same behavior.
Upvotes: 1
Reputation: 6461
Note: This answer is not an attempt give exact answer to the question. Instead, gives simple answer about optional or Default parameter to the method or constructor how that works in Typescript (Just for documentation purpose). Users whomever searched for simple optional / default values for the parameter may find this answer as helpful.
I'm using Typescript latest and stabled version as of June 2017 with VSCode.
We can achieve the optional parameter and default value by using the below approach.
export class Searcher {
createLog(message: string = "Default: No Message!") {
console.log(message);
}
}
Good reference link to learn about Optional and Default valued parameters in TypeScript.
Edit:
You can call the above method, like this.
this.createLog();
Therefore you don't need to pass any parameter value "it becomes optional parameter".
And when you don't pass the parameter "The default value will be supplied in that place of execution"
Hence We can achieve the optional parameter and default value of the pararmeter by using the above approach.
I hope this helps to someone!
Upvotes: 0
Reputation: 106660
You can use destructuring for this:
class Car {
color: number;
numberOfWheels: number;
constructor ({color, numberOfWheels = 4}: {color: number, numberOfWheels?: number}) {
this.color = color;
this.numberOfWheels = numberOfWheels;
}
}
Alternatively...
constructor (options: {color: number, numberOfWheels?: number}) {
let {color, numberOfWheels = 4} = options;
this.color = color;
this.numberOfWheels = numberOfWheels;
}
Upvotes: 6
Reputation: 13211
Use if (options.numberOfWheels === void 0) { options.numberOfWheels = 4; }
instead. (Otherwise 0 or NaN ... will be 4 as well)
Aside that what you do is actually pretty clever and it's the best you can do. Things like that won't work:
constructor (options: {color: number, numberOfWheels?: number} = {color: options.color})
Upvotes: 4