-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Description
TypeScript Version: 3.4.2
Search Terms:
Generic variable vs generic function
Generic composition
Generic inference parameter in function
Code
Let's consider this code
/**
* Extract object props by a specific value
*/
type PickByValue<T, ValueType> = Pick<T, {
[Key in keyof T]: T[Key] extends ValueType ? Key : never
}[keyof T]>;
/**
* Map keys by value-compatibility. Non-recursive.
*/
type MatchKeys<T, U> = {
[P in keyof T]: keyof PickByValue<U, T[P]>;
};
/**
* Like keyof, but for value
*/
type ValueOf<A extends Object> = Exclude<A[keyof A], undefined>;
/**
* The src props we have
*/
export interface SrcPropsInterface {
srcAttributeString: string;
srcAttributeNumber: number;
}
/**
* The dest props we want to apply
*/
export interface DestPropsInterface {
destAttributesString: string;
anotherAttributesString: string;
destAttributesNumber: number;
anotherAttributesNumber: number;
}In a variable approach, the following code works perfectly with all completion:
/**
* Here it's perfectly constraint
*/
type Combi = Partial<MatchKeys<SrcPropsInterface, DestPropsInterface>>;
const comb: Combi = {
srcAttributeNumber: "anotherAttributesNumber",
srcAttributeString: 'destAttributesString',
// toto: 'test' // Error as expected
};With a generic function approach, we get some weird things:
declare const obj: DestPropsInterface;
// Try generic way + inference
/**
* Return a object that exposed the missing attributes that still need to be fill somewhere
*/
function map<
DestProps,
T extends Partial<MatchKeys<SrcPropsInterface, DestProps>>
>(data: DestProps, map: T) {
// Consider the Proxy code here
const obj = {} as Pick<DestProps, Exclude<keyof DestProps, ValueOf<T>>>;
return obj;
}Case 1: no completion anymore
const target = map(obj, {
});
But as soon as I write blindly a known attribute from SrcPropsInterface, the type checking work:
const target = map(obj, {
srcAttributeNumber: 'anotherAttributesNumber',
srcAttributeString: 'badValue' // error as expected
});Case 2: any-like
We can put any kind of value and there is no complaint (and naturally no completion):
const target = map(obj, {
srcAttributeNumber: 'anotherAttributesNumber',
foo: 'bar',
bar: 42
});Case 3: no output anymore
As soon as the map object is correctly fill, the target object get correctly all remaining attribute:
const target = map(obj, {
srcAttributeNumber: 'anotherAttributesNumber',
});
target.anotherAttributesString; // Ok
target.destAttributesNumber; // Ok
target.destAttributesString; // Ok
target.anotherAttributesNumber; // Error as expected: we already remove it
But if we provide a wrong map because the type checker doesn't provide the correct feedback, target is converted as never:
const target = map(obj, {
srcAttributeNumber: 'anotherAttributesNumber',
foo: 'bar'
});Expected behavior:
Case 1: get correct type checker in the map parameters like for the variable approach
Case 2: I suppose it's just a side-effect of Case 1, but I expect to have TypeScript wiping me by attempting to introduce unknown parameters.
Case 3: I suppose it's just a side-effect of Case 1 and 2.
Actual behavior:
Check Case 1, Case 2 and Case 3: no type checking on the generic map parameter whereas in a variable approach, it works.
Playground Link:
Playground @ 2019-04-08
Related Issues:
#28938


