简体   繁体   中英

Typescript - How to get a subset of properties from an object into a variable based on an interface

I have a function that receives an intersection of two types:

interface A {
  propA: string;
}
interface B {
  propB: number;
  propC: boolean;
}
function C(props: A & B) {
}

Now, inside the function body I would like to get objects containing only the subset of properties from each of the interfaces; so I was wondering if Typescript had any utility to achieve that:

function C(props: A & B) {
  const a = fancyPicker<A>(props);
  const b = fancyPicker<B>(props);
  console.log(a);
  // prints "{ propA: "some string" }"
  console.log(b);
  // prints "{ propB: 42, propC: false }" 
}

You're after a function that iterates over a known set of property names - this can't be done in pure TypeScript because TypeScript uses type-erasure, so the runtime script has no-knowledge of what the set of property names is.

But using a TypeScript compile-time extension known as a Custom Transformer, specifically ts-transformer-keys the TypeScript compiler will emit property-name lists that can be used.

Here's something that works, but isn't perfect because it doesn't use the types of each property - it only matches names:

import { keys } from 'ts-transformer-keys'; // <-- This module is where the magic happens.

type IndexableObject = {
    [key: string]: any
};
/*
* The `IndexableObject` above is a hack. Note it allows `any` property type. Ideally it'd be something like this instead, but using `keyof` in a type indexer is not yet supported: https://github.com/microsoft/TypeScript/pull/26797
*

type IndexableObject<TOther> = {
    [key: TKey extends keyof TOther]: PropertyType<TOther,TKey>
};

*/

function fancyPicker<TSubset extends object>(superset: IndexableObject): Partial<TSubset> {

    const subsetPropertyNames = keys<TSubset>();

    const ret: Partial<TSubset> = {
    };

    for (const subsetPropertyName of subsetPropertyNames) {
        const propName: string = subsetPropertyName as string; // <-- This is also a hack because property keys/names are actually `string | number | symbol` - but this function assumes they're all named properties.
        if (propName in superset) {
            const value = superset[propName];
            ret[subsetPropertyName] = value;
        }
    }

    return ret;
}

Usage (using your example):

interface A {
  propA: string;
}

interface B {
  propB: number;
  propC: boolean;
}

type ABIntersection = A & B;
type ABUnion        = A | B;

function C(props: ABIntersection) {
  const a = fancyPicker<A>(props);
  const b = fancyPicker<B>(props);
  console.log(a);
  // prints "{ propA: "some string" }"
  console.log(b);
  // prints "{ propB: 42, propC: false }" 
}

const testValue = { propA: "some string", propB: 42, propC: false, propD: "never see this" };
C(testValue);

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