Skip to content

Function fields in class with conditional optionality of parameters #57139

Closed
@BenjaBobs

Description

@BenjaBobs

🔎 Search Terms

"class", "conditinal types", "conditional functions parameters", "optional function parameters"

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about classes

⏯ Playground Link

https://www.typescriptlang.org/play?ts=5.4.0-dev.20240123#code/MYGwhgzhAEAqCmEAuBhcUA8sCSA7ADgK5IB80A3gFDTQD0tcAnvvNAOQAUYATgOYQB+AFxw8RJAEpoAXjIA3APYBLACZtoSmLgVJokCEt64wAIxCskC6NToMkzVmxwFi0eAA8k8XCpiEf8ABmSrjwKtAC0Fx8giLO4tAAPtD+KkEhYVKy0Iqq0CLR-HFixFnyymo2gQoKxS66Hl4+fgHBoSo2NJGFsaL1ZTkVnflRPEV94gO54TbSozHCE6Ui0zJk5AC+lFuUwAq4yNAK+EhK+2AgMtChAO5wiKjoEBip6e0kHBIA3JTHp+cgAB01QUny+tmgAHkANIAGj0MA8LGAXg6u32h248AAjoQlFjwnNbvdkGh9BhcIQALYmeDcD7fShY3H4sLAmpgiEAUW43AU3HhY2p3l01W4ehi0BukGuOmg+D5clUYUFiPcyNRTJxeIJ7NBAEZvhCYaq3Or4CiwpRKPRoNhdGB8CweDBLFL+QBrI7EAxpI6BaCgfSB-ZeTyUewsKEnLBkOY9RawKYVH6R1gAJRxsauPTiydUP3RBwdIkhMcpNLpcfm-EWFdp3AGmx+e2L0BMIkz2Ip1Ib1YTInrdKbG0LaegKAAFhaPdm5rAzU1fClWhlwpEy0hs52s7ASC2MbpgCIpzOXqv3jmxr0h421hRR0XDioT9PgLPb-3r3Xe8P783rVtABNBRCEDMBcClbhHQRPRcEYI5IKQacNFwJRTguHILkIeBdieElHn0AAmLASlICgbBMHhXzPeJiC-BY6kmf8thoKiAC8aPfUj6n7AclkkFipBlCDGG2a1W0OP4zmMEAiKuYkEFJJ4SNeNowgZH5pIBIjASo7hOVtE1YKRC1NW02TdI4wyGGMmVTMtNFJN0ZkdTCeSiXgO4lMIqASM-MEtRZAkrJ4GzoB5PkBQlXhhVwUV+RimBpS0OUFQUJU0hUU0HM1VzWRUUL2PCyL+UFPg4oS8VrylGVtF0dLMpVEzzUcoK3MKvSwsNcEjLhFqNStfKQq64qeuNfr7NazUgA

💻 Code

class TestClass<TInput> {
  // Type '(args?: TInput) => void' is not assignable to 
  // type 'TInput extends undefined ? (args?: TInput | undefined) => void : (args: TInput) => void'
  foo: TInput extends undefined
    ? (args?: TInput) => void
    : (args: TInput) => void 
  = (args?: TInput): void => {}
}

const optional = new TestClass<undefined>();
optional.foo(); // OK, as expected

const required = new TestClass<number>();
required.foo(); // Error, argument for args was not provided, as expected
required.foo(1); // OK, as expected

// It appears to work outside of class context
type Opt<T> = (args?: T) => void;
type Req<T> = (args: T) => void;

const a: Opt<number> = (args?: number) => {};
const b: Req<number> = (args?: number) => {};

type Check<T> = T extends undefined ? Opt<T> : Req<T>;
const c: Check<undefined> = (args?: number) => {};
const d: Check<number> = (args?: number) => {};

// You can wrap as any on the initial value
class TestClass2<TInput> {
  // Type '(args?: TInput) => void' is not assignable to type 'Check<TInput>'.
  bar: Check<TInput> = (args?: TInput) => {}
  baz: Check<TInput> = ((args?: TInput) => {}) as any
}

const optional2 = new TestClass2<undefined>();
optional2.bar(); // OK, as expected
optional2.baz(); // OK, as expected

const required2 = new TestClass2<number>();
required2.bar(); // Error, argument for args was not provided, as expected
required2.baz(); // Error, argument for args was not provided, as expected
required2.bar(1); // OK, as expected
required2.baz(1); // OK, as expected

🙁 Actual behavior

class TestClass<TInput> {
  // Type '(args?: TInput) => void' is not assignable to 
  // type 'TInput extends undefined ? (args?: TInput | undefined) => void : (args: TInput) => void'
  foo: TInput extends undefined
    ? (args?: TInput) => void
    : (args: TInput) => void 
  = (args?: TInput): void => {}
}

🙂 Expected behavior

class TestClass<TInput> {
  // This should be possible
  foo: TInput extends undefined
    ? (args?: TInput) => void
    : (args: TInput) => void 
  = (args?: TInput): void => {}
}

I would expect this to work because it works outside of class definitions, and because having the implementation function args be optional should be able to handle both cases.

Additional information about the issue

Might be related to #12400

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions