Edit: Updated, minimal example (hopefully):
Paring it all down, I guess at the heart of my curiosity is why this won't work, and causes the two errors below:
interface Implicit {
someNumber?: number | { value: number }
someString?: string | { value: string }
}
interface Explicit {
someNumber?: { value: number }
someString?: { value: string }
}
const impl:Implicit = {
someNumber: 123,
someString: { value: 'impl' }
}
function convert (obj: Implicit) {
const expl:Explicit = {}
let k: keyof typeof obj;
for (k in obj) {
const objk = obj[k] // also, why does this alter behavior?
if (typeof objk === 'object') {
expl[k] = objk // Error {value:string}|{value:number} not assignable to {value:string}&{value:number}
} else {
expl[k] = { value: objk } // Error string|number is not assignable to never
}
}
}
Is TS not able to determine whether k
is someNumber
or someString
each loop, so it's creating an intersection
enforcing the result to be both?
Previous question:
I'm trying to create a versatile function that accepts a shorthand, implicit notation { someParameter: 123 }
or an explicit version: { someParameter: { value: 123, min: 0, max: 200 }}
, and converts and returns the explicit version:
interface ImplicitValues {
color: number | string | ExplicitValueColor;
position: number | ExplicitValuePosition;
}
interface ExplicitOptions {
min?: number
max?: number
}
interface ExplicitValueColor extends ExplicitOptions {
value: number | string
}
interface ExplicitValuePosition extends ExplicitOptions{
value: number,
}
There are other ImplicitValue types as well (not listed for brevity), so I'd like to create a generic type, that converts to the explicit type automatically:
type MakeExplicit<T> = {
[K in keyof T]?: Extract<T[K], { value: any }>
}
This seems to work, as when we use mouse over the type using @jcalz awesome Expand utility, it is correct:
However, when we try to loop through the ImplicitValues array, and make them explicit, we get the following error:
const someObject:ImplicitValues = {
color: 0xff0000,
position: {
value: 123,
min: 0,
max: 200
}
}
function isExplicit <T>(param: any): param is MakeExplicit<T> {
if (typeof param === 'object') return 'value' in param;
return false;
}
function convertToExplicit(obj:ImplicitValues) {
let explicitValues:MakeExplicit<ImplicitValues> = {};
let key:keyof typeof obj
for (key in obj) {
if (isExplicit(obj[key])) {
explicitValues[key] = obj[key]
// ERROR: Type 'string | number | ExplicitValueColor | ExplicitValuePosition' is
// not assignable to type 'ExplicitValueColor & ExplicitValuePosition'.
} else {
explicitValues[key] = { value: obj[key] }
// ERROR: Type 'string | number | ExplicitValueColor | ExplicitValuePosition' is
// not assignable to type 'number'.
}
}
return explicitValues
}
Setting these values manually works fine, but not dynamically in a loop. Do I cast explicitValues[key] = (obj as any)[key]
and get it over with, or is there a better way to do this?
Link to TS playground, if that's helpful.