1

I would like to construct a type from this object:

const isSynchronized: Record<SynchronizableField, boolean> = {
    /* synchronized */
    surveyMacroEnvironments: true,
    coordinateReferenceSystemCrs: true,
    transactionType: true,
    epsgTransformation: true,
    startingAgreementDate: true,
    expirationAgreementDate: true,
    transactionTypeNotes: true,
    surveyDataType: true,
    /* not synchronized */
    surveyName: false,
    validationStateCd: false,
    legacy: false,
    notifyOnCreate: false,
    notifyOnValidate: false,
    finalReportLink: false,
    // timestamp fields
    creationDate: false,
    lastUpdate: false,
    // continent and country are handled differently
    continent: false,
    country: false,
};

where the type needs to have only the keys with values equal to true, could you please help me or give me any suggestions?

Thanks

Nasian
  • 17
  • 5
  • Does [this approach](https://tsplay.dev/mAx3ZN) meet your needs? If so I'll write up an answer explaining; if not, what am I missing? – jcalz Jun 01 '23 at 14:58
  • @jcalz yeah that was exactly the approach I was looking for Thanks a lot! – Nasian Jun 03 '23 at 08:59

1 Answers1

1

As a first step we have to remove that type annotation on isSynchronized; we need the compiler to infer its type and then use that inferred type to compute the key set you're looking for. You could use the satisfies operator instead to make sure the property types are checked against and constrained to boolean:

const isSynchronized = {
    surveyMacroEnvironments: true,
    coordinateReferenceSystemCrs: true,
    transactionType: true,
    epsgTransformation: true,
    startingAgreementDate: true,
    // ✂ ⋯ ✂
    lastUpdate: false,
    continent: false,
    country: false,
} satisfies Record<string, boolean>;

type IsSynchronized = typeof isSynchronized;

Now you can inspect IsSynchronized to get the desired type.


You're looking for an application of a type function I call KeysMatching<T, V>, as requested in microsoft/TypeScript#48992 and as discussed in In TypeScript, how to get the keys of an object type whose values are of a given type?. The idea is that KeysMatching<T, V> would evaluate to the union of property keys of T where the property values at those keys are assignable to V. Specifically it looks like you want KeysMatching<IsSynchronized, true>.

There's no native KeysMatching provided by the language, but there are a number of ways to implement it yourself, with various issues and edge cases. One approach is a distributive object type where we map over all the properties of T and then index into the result with all the keys to end up with the union of the computed property types. Like this:

type KeysMatching<T, V> =
    { [K in keyof T]: T[K] extends V ? K : never }[keyof T]

And let's use it:

type SynchronizedKeys = KeysMatching<IsSynchronized, true>;
// type SynchronizedKeys = "surveyMacroEnvironments" | "coordinateReferenceSystemCrs" |
//   "transactionType" | "epsgTransformation" | "startingAgreementDate" | 
//   "expirationAgreementDate" | "transactionTypeNotes" | "surveyDataType"

Looks good. If you don't want to keep KeysMatching around, you can inline the definition to compute SynchronizedKeys directly:

type SynchronizedKeys = {
    [K in keyof IsSynchronized]: IsSynchronized[K] extends true ? K : never
}[keyof IsSynchronized];

Playground link to code

jcalz
  • 264,269
  • 27
  • 359
  • 360