Skip to content

Instantly share code, notes, and snippets.

@andyedinborough
Created March 28, 2018 13:57
Show Gist options
  • Select an option

  • Save andyedinborough/cf1dca5844e86620d183e09dea47d125 to your computer and use it in GitHub Desktop.

Select an option

Save andyedinborough/cf1dca5844e86620d183e09dea47d125 to your computer and use it in GitHub Desktop.

Revisions

  1. andyedinborough created this gist Mar 28, 2018.
    72 changes: 72 additions & 0 deletions ConditionalTypes.tsx
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,72 @@
    import * as React from 'react';

    type Parse<T> = { parse: (value: string) => T };
    type MaybeParse<T> = T extends string ? {} : Parse<T>;

    interface GenericTextboxBaseProps<TModel, TProperties extends keyof TModel> {
    model: TModel;
    name: TProperties;
    display?: (prop: TModel[TProperties]) => string;
    }

    type GenericTextboxProps<TModel, TProperties extends keyof TModel, TProperty extends TModel[TProperties]> =
    GenericTextboxBaseProps<TModel, TProperties> & MaybeParse<TProperty>;

    interface GenericTextboxState {

    }

    export class GenericTextbox<
    TModel,
    TProperties extends keyof TModel,
    TProperty extends TModel[TProperties]
    > extends React.Component<GenericTextboxProps<TModel, TProperties, TProperty>, GenericTextboxState> {
    render () {
    const { model, name, display } = this.props;
    return (
    <input
    name={name}
    value={display ? display(model[name]) : String(model[name] || '')}
    onChange={this.handleChange}
    />
    );
    }

    private handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const { value } = e.currentTarget;
    const { model, name } = this.props;

    const { parse } = this.props as GenericTextboxProps<TModel, TProperties, TProperty> as Parse<TProperty>;

    if (parse) {
    model[name] = parse(value);
    } else {
    model[name as string] = value;
    }
    };
    }

    interface Person {
    name: string;
    age: number;
    }

    const person: Person = { name: 'Steve', age: 34 };

    export const test = () => (
    <>
    <GenericTextbox model={person} name="name" />
    <GenericTextbox model={person} name="age" parse={(v: string) => parseInt(v, 10) || 0} />

    {/*
    error: dne doesn't exist in person
    <GenericTextbox model={person} name="dne" />
    error: parse prop missing
    <GenericTextbox model={person} name="age" />
    error: parse return type doesn't match person.age
    <GenericTextbox model={person} name="age" parse={v => new Date()} />
    */}
    </>
    );