fabian-hiller / valibot

The modular and type safe schema library for validating structural data 🤖
https://valibot.dev
MIT License
6.32k stars 204 forks source link

Infer type from variant object #900

Closed jojojojojoj5564656465465 closed 3 weeks ago

jojojojojoj5564656465465 commented 3 weeks ago

https://valibot.dev/playground/?code=JYWwDg9gTgLgBAKjgQwM5wG5wGZQiOAcg2QBtgAjCGQgbgCh6BjCAO1XgDVkphlWYAZSYALAKYhkcALyYAdCV78YACkIwAnmDGEANHADa9OPIgUAVmKaqA3sZNxN2gFzzyMMVDJqJyYKUIASl17E19-Vww5MGBtFSiOXlYAcxVg+XDSNODQuFZkEDFnBJgk1MD7AF8ckyizS2sVOwdHLSK3YA8vLMIAVygAmod+0kjo2LF4uUTgFLT9KJHskJNqldMLK1tcp3ao909vQgATZA8g9ZNTjzGYuJKy+flgVAgAETPJwKG1+gBdQIMei7ODcJQCAAqbRk8gAkqxsJ54WBejAADy7CDYUE8PgCYTiSQAPiBjBY7HgUDEqF6pBgzjBeJgUO0MKiYB4qEmjOUBN8+macEyrkI5n4YgAAmIAB4FMCkMRyFggPT2XYizKq6pwRjMNgcOD1GGC4VEMWsSUyuUKpX4VUmdVETUhSr0Kk0ulyTL0epeyT+IA

image

jojojojojoj5564656465465 commented 3 weeks ago

// In types.ts, let's first add some utility types

/**
 * Utility type to extract discriminator value
 */
type ExtractDiscriminatorValue<T> = T extends { [K in any]: infer U } ? U : never;

/**
 * Utility type to get the object corresponding to a discriminator value
 */
type ExtractVariantType<T, K extends string, V> = T extends { [key in K]: V } ? T : never;

/**
 * Modified VariantSchema to support discriminated type inference
 */
export interface VariantSchema<
  TKey extends string,
  TOptions extends VariantOptions<TKey>,
  TMessage extends ErrorMessage<VariantIssue> | undefined,
> extends BaseSchema<
    InferInput<TOptions[number]>,
    | InferOutput<TOptions[number]>,
    VariantIssue | InferVariantIssue<TOptions>
  > {
  readonly type: 'variant';
  readonly reference: typeof variant;
  readonly expects: 'Object';
  readonly key: TKey;
  readonly options: TOptions;
  readonly message: TMessage;

  // Add discriminated type for inference
  readonly _variantBrand: {
    [K in TKey]: {
      [V in ExtractDiscriminatorValue<InferOutput<TOptions[number]>[TKey]>]: 
        ExtractVariantType<InferOutput<TOptions[number]>, TKey, V>
    }
  };
}

/**
 * Modified parse function to support discriminated type inference
 */
export function parse<T extends BaseSchema<any, any, any>>(
  schema: T,
  input: unknown
): T extends VariantSchema<infer K, any, any>
  ? T['_variantBrand'][K][keyof T['_variantBrand'][K]]
  : InferOutput<T> {
  // ... rest of implementation
}

/**
 * Modified variant function to use the new types
 */
export function variant<
  const TKey extends string,
  const TOptions extends VariantOptions<TKey>,
>(key: TKey, options: TOptions): VariantSchema<TKey, TOptions, undefined>;

export function variant<
  const TKey extends string,
  const TOptions extends VariantOptions<TKey>,
  const TMessage extends ErrorMessage<VariantIssue> | undefined,
>(
  key: TKey,
  options: TOptions,
  message: TMessage
): VariantSchema<TKey, TOptions, TMessage>;

export function variant(
  key: string,
  options: VariantOptions<string>,
  message?: ErrorMessage<VariantIssue>
): VariantSchema<string, VariantOptions<string>, ErrorMessage<VariantIssue> | undefined> {
  return {
    kind: 'schema',
    type: 'variant',
    reference: variant,
    expects: 'Object',
    async: false,
    key,
    options,
    message,
    '~standard': 1,
    '~vendor': 'valibot',
    _variantBrand: {} as any, // Type safety is handled by the type system
    '~validate'(dataset, config = getGlobalConfig()) {
      // ... rest of existing implementation
    },
  };
}
fabian-hiller commented 3 weeks ago

This is the default behaviour of TypeScript and not related to Valibot. Learn more about discriminated unions in the TypeScript docs: https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions