I'd like to initialize my object passed into function in similar manner as it appears to scalar values, e.g.:
someFunction(myValue: number = 5) { }
So, i keep both type and default value. I'd like to apply similar construct to following:
someFunction(myObj: {val1: number, val2: number}) { }
If I'll go straight ahead and just do the no-brainier copy paste solution:
someFunction(myObj: {val1: number = 5, val2: number = 10}) {}
Each initialization approach gives a TypeScript erorr A type literal property cannot have an initializer.
. Ok, got it, I'm unable to mix type literals with initialization. Using analogy we have:
someFunction(myValue: number = 5) { }
^^^^^^^ ^^^^^^ ^^
| | +-- Initialization value
| +-- Type literal
+-- Parameter name
And analogically:
someFunction(myObj: {val1: number, val2: number} = {val1: 5, val2: 10}) {}
^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
| | +-- Initialization
| +-- Type literal
+-- Parameter name
Solution is to use way more verbose syntax:
someFunction(myObj: {val1: number, val2: number} = {val1: 5, val2: 10}) {}
The question is it possible, to have an cake and eat it: have concise syntax like in scalar value default value definition (number = 5)
and full type control with object definition?
PS I request bonus points for ASCII art :)
If you take advantage of desctructuring, you can reduce a lot of the duplicate code, however it is important to be aware of the differences. Here's the pattern I generally use.
function fun({ val1 = 1, val2 = 2 } = {}) {
return val1 + val2
}
The signature of this function is correctly inferred as function fun({ val1, val2 }?: { val1?: number; val2?: number; }): number
.
However it is important to note that this function differs in a couple important ways from the original.
fun({ val1: 2 })
will result in val2
still being the default value, 2
If you need a required parameter (in that if you provide anything, you must provide it), you have to go back to specifying everything, which quickly becomes even more messy since you have initializers in two locations. This can be mitigated by using an interface to describe the type.
interface Args {
val1?: number
val2?: number
val3: string
}
function fun({ val1 = 1, val2 = 2, val3 }: Args = { val3: ' ' }) {
return val3.repeat(val1 + val2)
}
There is no built-in way to do this. The simplest solution would be to let the compiler infer the parameter type based on the default value:
function someFunction(myObj = { val1: 5, val2: 10 }) { }
The problem with this solution is that all properties are required on the parameter, so you would have to specify them even if their value would be undefined.
To get around this we can define a helper function that returns a type with all items marked as optional:
function params<T>(o: T): Partial<T> {
return o;
}
function someFunction2(myObj = params({ val1: 5, val2: 10, otherValueWithoutDefault: null as number })) { }
Or a version that supports both optional and required parameters:
function params<TRequired>() : <TOptional>(o: TOptional) => Partial<TOptional> & TRequired
function params<TOptional>(o: TOptional) : Partial<TOptional>
function params<T>(o?: T): Partial<T> | (<TOptional>(o: TOptional) => Partial<TOptional> & T) {
if(o != null) {
return o;
} else {
return function(o : any) { return o; } as any
}
}
function someFunction2(myObj = params<{ required: boolean}>()({ val1: 5, val2: 10})) { }
someFunction2({
required: true,
val1: 0
})
Looks like object literal as a param with default value is behaving different as compare to scalar function param.
A type literal property cannot have an initializer
This error occurs when try to provide a value when typing an object. Hence, to get rid of this we can separate the typing from default values.
function someFunction({val1 = 5, val2 = 8} : { val1?: number; val2?: number }) {
console.log(val1);
console.log(val2);
}
someFunction({}) // 👉️ 5, 8
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With