简体   繁体   中英

can TypeScript restrict keyof to a list of properties of a particular type?

so, I want to write a generic method. For the sake of the question, let's say I want to concatenate 2 string properties from 2 different objects of the same type.

function concat<T>(target1: T, target2: T, key: keyof T): string {
  return target1[key] + target2[key];
}

The above fails to compile because the compiler has no indication that I want to restrict key to only be the list of properties of type string .

Is this not currently supported in TypeScript?

I know I can do runtime checks, but I want compile-time checks for this one.

You want a three parameter generic here. One for each input, and one for the key.

type CommonKey<A, B> = keyof A & keyof B

function concat<A, B, K extends CommonKey<A, B>>(
  target1: A,
  target2: B,
  key: K
): string {
  return `${target1[key]}${target2[key]}`
}

This defines the key as both a key of A and B, meaning that it must satisfy both.


To constrain it further to only string values, you need to use a mapped type with a condition to test for its type.

// This type returns all keys that have a value of type string
type StringKeyOf<T> = {
    // for all keys in T
    [K in keyof T]:

        // if the value of this key is a string, keep it. Else, discard it
        T[K] extends string ? K : never

// Get the union type of the remaining values.
}[keyof T]

// Only allow common keys of both objects.
type CommonKey<A, B> = StringKeyOf<A> & StringKeyOf<B>

function concat<
    A,
    B,
    K extends CommonKey<A, B>
>(target1: A, target2: B, key: K): string {
    return `${target1[key]}${target2[key]}`
}

Now this will give a type error, because age is a number.

concat(
    { name: 'a', age: 12 human: 'maybe?' },
    { name: 'b', age: 8, dog: true },
    'age' // Type error

)

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