-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Conditionally optional/conditionally readonly properties #44261
Comments
This seems at least combinatorially explosive (maybe undecidable?) when performing inference from a concrete type to a interface Calculate<T> {
x(? if T extends Calculate<A>): U
y(? if T extends Calculate<B>): V
} If the answer to that is "In practice there will be very few of these conditions" then it seems like not much is being gained over |
I hadn't considered recursive constraints - for my purposes I certainly wouldn't need those, so just rejecting them would be fine. That would be the same as writing type Recursive<T> = Recursive<T> extends { foo : string } ? {foo: T} : number;
//// ^^^^^^^^^ Type alias 'Recursive' circularly references itself.(2456) A specific example that clearly can't be rewritten as just a union would be something like: type ForString<T extends string> = {
value: T;
render: (item: T) => Result;
key?: (value: T) => string; // optional
}
type ForAny<T> = {
value: T;
render: (item: T) => Result;
key: (value: T) => string; // required
} you can't write type ForBoth<T> = ForString<T> | ForAny<T>; // Type 'T' does not satisfy the constraint 'string'.(2344) and writing type ForBoth<T extends string> = ForString<T> | ForAny<T>; defeats the purpose of having So instead you could write type ForBoth<T> = T extends string ? ForString<T> : ForAny<T>; I can't just use a non-generic |
I would very much in favour of this feature though for maybe only a subset of this. Currently the solution requires switching to My use case is having two "sides" depending on a type parameter. For example: interface Arguments<Namespaced extends boolean> {
...
namespace(? if Namespaced !extends true): Namespaced extends true
? string
: Namespaced extends false
? undefined
: string | undefined;
} |
also good for : export type MayHaveUsefulFoo<MyFoo> = {
// ... some other useful props
foo(? if MyFoo extends undefined | null | void): MyFoo
} instead of export type MayHaveUsefulFoo<MyFoo> = {
// ... some other useful props
} & (MyFoo extends undefined | null | void
? { foo?: MyFoo }
: { foo: MyFoo }) |
The current solutions looks quite ugly indeed. export type StoryAnnotations<
TFramework extends AnyFramework = AnyFramework,
TArgs = Args,
TArgsAnnotations extends Partial<TArgs> = Partial<TArgs>
> = BaseAnnotations<TFramework, TArgs> & {
/**
* Override the display name in the UI (CSF v3)
*/
name?: StoryName;
/**
* Override the display name in the UI (CSF v2)
*/
storyName?: StoryName;
/**
* Function that is executed after the story is rendered.
*/
play?: PlayFunction<TFramework, TArgs>;
/** @deprecated */
story?: Omit<StoryAnnotations<TFramework, TArgs>, 'story'>;
} & ({} extends TArgsAnnotations ? { args?: TArgsAnnotations } : { args: TArgsAnnotations }); It also forces you to rewrite interfaces into types with intersections. |
I was looking for something simple, human readable, and also understanable to the naked eye for making a parameter available on a type : using your "AVOID instead of" I achieved this:
ofcourse its for another purpose already, this type it self for me is a type generator for some functions, |
Conditionally-Optional Properties (in object types and interfaces)
π Search Terms
These issues are related, but not exactly the same:
β Viability Checklist
My suggestion meets these guidelines:
β Suggestion / π Motivating Example
A new syntax and associated type-checking making it possible to mark a property as "conditionally-optional", for example:
the intent here is that
getKey
is optional ifT
isstring
or a subtype ofstring
, but otherwisegetKey
is mandatory. Thus, an implementation could (for example) provide a default implementationgetKey = str => str
.It's currently possible to write types that accomplish this using a mixture of intersections and conditionally-mapped types. However, you lose certain important ergonomic attributes:
extend
ed whereas interfaces and object types can beπ» Use Cases
In general, any attribute of a property could be made conditional in the same way:
In most cases, a basic (but incomplete) workaround exists: use the less-restrictive form everywhere. For example, if it's going to mostly be used and you don't want to forget to pass a property, just make it required; if it's mostly going to be accessed and you don't want to forget that a property is present, just make it optional, etc.
Allowing these values to be set conditionally would just make it easier to express certain more-complex domain-specific constraints, while keeping the types mostly self-contained and readable.
The text was updated successfully, but these errors were encountered: