The issue is that you’re dynamically trying to set keys/values on an object based on another object. And when you do like:
if (params[param]) {
outputParams[param] = params[param];
You’re saying:
if there is a truthy value when I use the string param
(which I’ve coerced to force TS to pretend is a key of params
) to access params
, then set a property in ouputParams
to that key + the value in params
.
You aren’t saying “if the value of params[param]
is exactly the same as what it is expected to be”. You’re just saying “if this value is a truthy value then set it whatever it is”, but the object upon which you’re setting it expects specific types of things, not just “any allowed truthy value”
OutputParams
is an object that doesn’t have anything to do with InputParams
. All TS knows is that you’re trying to set one of the values in inputParams
as a value in outputParams
. Those values are going to be one of string
, string[]
and undefined
.
You can use the same type for input/output (just Params
, which I think is actually what you want but I might be wrong). And you can set them directly, not dynamically in a loop. That will the typing much easier: you are using it as a dictionary, and setting the properties directly means you can ensure the types being set are correct.
You can use Object.keys
et al to loop, but the issue is that JS specifies the return value of keys
is an array of strings, and TS has to abide by that. You can fix this in your project by overwriting the type of Object.keys
like (note this is copied from here):
type ObjectKeys<T> =
T extends object ? (keyof T)[] :
T extends number ? [] :
T extends Array<any> | string ? string[] :
never;
interface ObjectConstructor {
keys<T>(o: T): ObjectKeys<T>
}
(you would put that somewhere in your project in a file like my-types.d.ts
)
This doesn’t fix one of the major issues you are going to have. Query params are a string. But the keys and values of that interface are typed. Types only exist prior to compile time. TS knows that there is absolutely no way it can check the types are correct. Anything of that ilk (parsing JSON has same issue) is outside of the type system. So you either have to bypass the type system: this is what type assertions do, eg as keyof Params
, or you can use a more generic type (Record<string, string>
for example), or you can use a validation library (not typescript-specific, but more modern ones generally provide methods for taking a validation schema & converting it to TS types in your code).
Again, the logic here I can’t quite figure out. Can you explain a little bit more what you’re trying to do – ie show a more realistic example? InputParams
and OutputParams
seem like they should be the same thing, and I’m somewhat confused as to the reason for paramKeys
existing, particularly as it’s hardcoded.