Home > database >  Prevent values of a certain type in an object
Prevent values of a certain type in an object

Time:10-06

I have a type SecretString which can contain any kind of sensitive information that I may handle on my server but which I don't want to accidentally log out. It's a branded string type:

type SecretString = string & {__brand: "secret"}

My logging function takes a string and an object (any, currently) which can contain whatever additional information might be necessary. Is there a way for me to express that second parameter as a type that can be an arbitrary object but will complain if a SecretString is present? Nested checking would be great but I would be happy with first level.

As the SecretString works just like a string, it typically comes from a function like this one:

function decryptString(encryptedString: EncryptedString): SecretString {
  // ...
}

So this is an example of a scenario where I would like an error message:

const keys = decryptString(encryptedKeys);

logger.info('This should not compile!', { 
  valueThatIsOk: 'this is ok', 
  valueThatShouldFail: keys 
});

CodePudding user response:

Recursively is beyond my TypeScript at present, but at the top level a mapped type should work:

type SecretString = string & {__brand: "secret"};

function log<ObjType>(msg: any, obj: ObjType extends SecretString ? never : ObjType) {
    console.log(msg, obj);
}

let x: SecretString = "example" as SecretString;

log("hi", {example: 42});
log("hi", "more info");
log("hi", x);
//        ^−−−−−−−−−−−−−−− error as desired

Playground link

For passing an object instead, it's still the same thing but in an object structure:

function log<ObjType extends object>(
    msg: any,
    obj: {[key in keyof ObjType]: ObjType[key] extends SecretString ? never : ObjType[key]}
) {
    console.log(msg, obj);
}

Playground link

(If you want both, you can combine them in a union.)

  • Related