Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function fields in class with conditional optionality of parameters #57139

Closed
BenjaBobs opened this issue Jan 23, 2024 · 2 comments
Closed

Function fields in class with conditional optionality of parameters #57139

BenjaBobs opened this issue Jan 23, 2024 · 2 comments
Labels
Duplicate An existing issue was already created

Comments

@BenjaBobs
Copy link

🔎 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

@RyanCavanaugh
Copy link
Member

See #30639 / #46429 - the conditional type cannot be distributive. You need to write

class TestClass<TInput> {
  foo: [TInput] extends [undefined]
    ? (args?: TInput) => void
    : (args: TInput) => void 
  = (args?: TInput): void => {}
}

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Jan 23, 2024
@BenjaBobs
Copy link
Author

Ah yeah, that works, hadn't stumbled upon that syntax yet. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

2 participants