- 
                Notifications
    You must be signed in to change notification settings 
- Fork 13.1k
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.