简体   繁体   中英

Flowtype/Typescript general purpose get property

Is there any way to properly type check the following code in either flow or typescript?:

type A = {
    x: string
}
type B = {
    y: string
}
function get(obj, prop) {
    return obj[prop];
}
const a: A = { x: 'x' }
const b: B = { y: 'y' }

get(a, 'x') // should type check
get(b, 'y') // should type check
get(a, 'y') // should NOT type check
get(b, 'x') // should NOT type check

Where get is a general purpose function for obj of any type. Can we annotate the code in a way that flow will check if obj has prop ?

The main use case is writing a general purpose get function for deep properties. Something with similar functionality as _.get . I'm trying to avoid this situation:

if (a && a.b && a.b.c && a.b.c.d === 'blah') { ... }

EDIT:

As mentioned by @vkurchatkin, we can use $Keys . But I can only get that to work with a getter function that is 1 level deep. How do we type the following function:

get<T: {}>(obj: T, prop1: $Keys<T>, prop2: /* ! */): /* ! */ { ... }

EDIT 2:

I have written the following so far:

type A = {
    x: B
}

type B = {
    y: string
}

type GetDeep<T: {}, U, V> = Helper<T, U, V, Get<T, U>, Get<U, V>>

type Helper<T, U, V,   W, X> = (obj: T, a: $Keys<T>, b: $Keys<U>) => V

type Get<T, U> = (obj: T, a: $Keys<T>) => U;

// NOTE: here if I replace GetDeep<*, B, *> with GetDeep<*, *, *>
//       then it wrongly type checks everything
const getDeep: GetDeep<*, B, *> = (obj, a, b) => {
    return obj[a][b];
}

var va: A = {
    x: {y: 'abc'}
}

getDeep(va, 'x', 'y'); // ok
getDeep(va, 'x', 'z'); // error

It looks like in type Get<T, U> = (obj: T, a: $Keys<T>) => U , U is not the type of the value of obj[a] .

You can do it with Flow:

function get<T: {}>(obj: T, prop: $Keys<T>) {
    return obj[prop];
}

Unfortunately, returned type is inferred as any . Flow has currently has $PropertyType in the works, so I believe this should be possible in the future (it doesn't work as expected yet):

function get<T: {}, P: $Keys<T>>(obj: T, prop: P): $PropertyType<T, P> {
    return obj[prop];
}

With this type you will be able to go two levels deep:

function getDeep<
    T: {},
    P: $Keys<T>,
    R: $PropertyType<T, P>,
    P2: $Keys<R>
  >(obj: T, a: P, b: P2): $PropertyType<R, P2> {
    return obj[a][b];
}

Or make something composable.

Where get is a general purpose function for obj of any type. Can we annotate the code in a way that flow will check if obj has prop

You can't do that with TypeScript.

Something with similar functionality as _.get

It would be much easier if safe navigation operator becomes available : https://github.com/Microsoft/TypeScript/issues/16

However the JavaScript committee needs to get on it and as far as I can see its sadly not being championed by anyone.

Till then I've also done a && ab etc.

More

A slow function I've used sometimes when cleaning up other peoples code:

export function safe<T>(action: () => T): T | undefined {
    try {
        return action();
    }
    catch (ex) {
        return undefined;
    }
}

// Usage
let c = safe(()=>a.b.c);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM