Description
🔎 Search Terms
"jsdoc conditional properties", "typescript conditonnal properties", "type conditon"
🕗 Version & Regression Information
This is the behavior in every version I tried, and I reviewed the FAQ for entries about conditional types
⏯ Playground Link
💻 Code
interface ConditionCValFalse {
c?: false
}
interface ConditionCValTrue {
c: true
}
interface ConditionBValFalse {
b?: false
}
interface ConditionBValTrue {
b: true
}
interface Base {
a: string
};
const test = (config: Base & (ConditionBValTrue | ConditionBValFalse) & (ConditionCValTrue | ConditionCValFalse )) => false
const returnBool = (val: boolean) : boolean => val;
test({ a: "a", b: returnBool(true) }) // Type 'boolean' is not assignable to type 'true'
🙁 Actual behavior
When using a second set of conditional properties, the first one throws a type error while it passes when using the condition alone.
Argument of type '{ a: string; b: boolean; }' is not assignable to parameter of type 'Base & (ConditionBValFalse | ConditionBValTrue) & (ConditionCValFalse | ConditionCValTrue)'.
Type '{ a: string; b: boolean; }' is not assignable to type 'Base & ConditionBValTrue & ConditionCValTrue'.
Type '{ a: string; b: boolean; }' is not assignable to type 'ConditionBValTrue'.
Types of property 'b' are incompatible.
Type 'boolean' is not assignable to type 'true'.(2345)
If I remove the second condition, or that I passes true
or false
directly, I get not error:
const test = (config: Base & (ConditionBValTrue | ConditionBValFalse)) => false
const returnBool = (val: boolean) : boolean => val;
test({ a: "a", b: returnBool(true) })
const test = (config: Base & (ConditionBValTrue | ConditionBValFalse) & (ConditionCValTrue | ConditionCValFalse)) => false
const returnBool = (val: boolean) : boolean => val;
test({ a: "a", b: true })
Somehow, when passing the c
property – which is not supposed to be required – it passes:
const test = (config: Base & (ConditionBValTrue | ConditionBValFalse) & (ConditionCValTrue | ConditionCValFalse)) => false
const returnBool = (val: boolean) : boolean => val;
test({ a: "a", b: returnBool(true), c: true })
It also passes if I only declare the a
required property:
test({ a: "a" })
So b
and c
are indeed optional.
🙂 Expected behavior
I expect the type checking to be the same whether I passes one or multiple conditional properties. I believe all of these should work:
// Pass
test({ a: "a" })
test({ a: "a", b: true })
test({ a: "a", c: false })
test({ a: "a", b: returnBool(true), c: returnBool(false) })
test({ a: "a", b: returnBool(true), c: false })
test({ a: "a", b: true, c: returnBool(false) })
// Currently fail
test({ a: "a", c: returnBool(true) })
test({ a: "a", b: returnBool(true) })
Additional information about the issue
For the context, I'm trying to make some properties required based on other property values.
I realize that maybe there is better way to do this in TS, but actually my problem is in JSDoc, and I believe I'm more limited on what I can do with JSDoc synthax.
/**
* @typedef {object} ConditionCValFalse
* @property {false} [c]
*/
/**
* @typedef {object} ConditionCValTrue
* @property {true} c
*/
/**
* @typedef {object} ConditionBValFalse
* @property {false} [b]
*/
/**
* @typedef {object} ConditionBValTrue
* @property {true} b
*/
/**
* @typedef {object} Base
* @property {string} a
*/
/**
* @param {Base & (ConditionBValTrue | ConditionBValFalse) & (ConditionCValTrue | ConditionCValFalse)} config
*/
const test = (config) => false;
/**
* @param {boolean} val
* @returns {boolean}
*/
const returnBool = (val) => val;
test({ a: "a", b: returnBool(true) }); // Type 'boolean' is not assignable to type 'true'
My real code is on a React component, and unlike the example above, even passing all the optional properties throws the error.