-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Autoinference by optional generic #57277
Comments
You didn't really give me any concrete examples to work from, but I think what you want is this? type ContentTypeMapper = {
ID: string;
TEXT: string;
URL: string;
NUMBER: number;
}
type Field = MakeContentFields<keyof ContentTypeMapper>;
type MakeContentFields<K extends keyof ContentTypeMapper> = K extends unknown ? {
contentType: K;
value: () => ContentTypeMapper[K];
} : never;
function fn(f: Field) { }
// Error, as expected
fn( { contentType: "ID", value: () => 32 });
// OK, as expected
fn( { contentType: "ID", value: () => "foo" }); |
Thanks for the idea! So not exactly what i'm trying to achieve, it looks more like this: type ContentTypeMapper = {
ID: string;
TEXT: string;
URL: string;
NUMBER: number;
}
type ContentType = keyof ContentTypeMapper;
//pseudo ts, obviously not working
type Field <T = ContentType> = {
path: string,
contentType: T;
value: ( val: unknown ) => ContentTypeMapper[T]
}
type Schema = {
fields: Field[]
}
const mySchema: Schema = {fields: [
{
path: "name",
contentType: 'TEXT',
value: () => 8 //should trigger error
},
{
path: "age",
contentType: 'NUMBER',
value: () => 8 //this is fine
}
] as const} The only way I was able to achieve something like this was manually coding all the options and doing a union type, but having 20 elements on the ContentTypeMapper makes it too long and annoying, while a dynamic solution that understands the immutabilty of the schema would do it faster and cleaner. it s the 3rd time I come into a similar use-case where I need to use the value of one prop to type other prop. Obviously useless at runtime but all these cases happened with immutable schemas and a limited set of options. Another issue related to this, is that even doing the unions we get some issues, here is the real code of the ugly workaround: https://github.com/Blitzapps/blitz-orm/blob/main/src/types/schema/fields.ts type StringField = BormField & {
contentType:
| 'ID'
| 'COLOR'
| 'DATE'
| 'FILE'
| 'EMAIL'
| 'PHONE'
| 'URL'
| 'PASSWORD'
| 'LANGUAGE_TEXT'
| 'RICH_TEXT'
| 'TEXT';
default?: { type: 'fn'; fn: (currentNode: BQLMutationBlock) => string } | { type: 'value'; value: string };
validations?: {
enum?: string[];
unique?: boolean;
fn?: (value: string) => boolean;
};
};
type NumberField = BormField & {
contentType: 'DURATION' | 'HOUR' | 'RATING' | 'CURRENCY' | 'PERCENTAGE' | 'NUMBER_DECIMAL' | 'NUMBER';
default?: { type: 'fn'; fn: (currentNode: BQLMutationBlock) => number } | { type: 'value'; value: number };
validations?: {
enum?: number[];
unique?: boolean;
fn?: (value: number) => boolean;
};
};
//...
type AllDataField = StringField | NumberField | DateField | BooleanField;
export type DataField = BormField & {
shared?: boolean;
validations?: {
required?: boolean;
unique?: boolean;
};
isVirtual?: boolean;
dbConnectors?: [DBConnector, ...DBConnector[]];
} & AllDataField;
export type ContentType = keyof ContentTypeMapping;
export type ContentTypeMapping = {
ID: string;
JSON: unknown;
COLOR: string;
BOOLEAN: boolean;
///...
}
And this causes an issue with the enums: {
contentType: 'TEXT',
cardinality: 'ONE',
path: 'requiredOption',
default: { type: 'value', value: 'a' },
validations: { required: true, enum: ['a', 'b', 'c'] as string[] },
}, Which forces me to add the |
So you want to be able to write a generic type and then infer the type parameter from the value that's assigned to it in order to properly constrain the allowed values, is that correct? type Foo<T> = { foo: T, bar: T };
const x: Foo<~> = { foo: 1206, bar: 777 }; // ok
// x :: Foo<number>
const y: Foo<~> = { foo: 42, bar: "wut" }; // error (TS won't make artificial unions) If so, then this is a duplicate of #47755. You can accomplish this today using a pattern called a "constrained identity function", e.g. function makeFoo<T>(x: { foo: T, bar: T }) {
return x;
}
const x = makeFoo({ foo: 1206, bar: 777 }); // ok
const y = makeFoo({ foo: 42, bar: "wut" }); // error but of course it would be nice not to have to write the useless do-nothing function. |
Yes something like that would do, like an "optional" generic inferred from other properties. Thought about the function version, but as you can see by the example is a schema defined in a json format. I would like to have the users of the ORM only needing to use functions to define actual functions, like default values. While the rest should be plain objects. Should I close this issue and add some extra detail to #477555? |
This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
π Search Terms
Autoinference and optional generic
β Viability Checklist
β Suggestion
The only way to do something like this right now is to explicitly add T (which can perfectly be computed from T as long as Field is a readonly object, or to do all the possible combinations manually, which is tedious (imagine 20 different ContenTypes)
This feature would enable this to be computed directly without the need to add to each instance of Field, as T can be inferred by the value of its path "contentType"
it is not runtime as long as we have the schema with the fields as const and with readonly.
π Motivating Example
The one expressed
π» Use Cases
The text was updated successfully, but these errors were encountered: