Flow property is missing in mixed passed - despite the type annotation - flowtype

I have the following in my declarations file (included in my [libs]):
export type EtlFieldNoIdxT = {
name: Name,
purpose: Purpose,
}
export type EtlFieldT = {
idx: number,
...EtlFieldNoIdxT
}
And the following in my use of the types:
export const createEtlField = (
etlFields: { [Name]: EtlFieldT },
newField: EtlFieldNoIdxT,
) => {
if (etlFields === {}) {
throw new Error({
message: 'Cannot create a new etlField with an empty etlFields',
});
}
const field: EtlFieldT = {
idx: maxId(etlFields, 'idx') + 1,
...newField,
};
const subject: Name = Object.values(etlFields).find(
(f) => f.purpose === 'subject', // <<< f.purpose "missing in mixed" error
).name; // <<< .name "missing in mixed" error
return newEtlField(field, subject);
};
Despite having annotated the input, can flow not infer the type of what Object.values would thus return?
Thank you in advance for pointing out my misunderstanding.
- E

If you check the declaration for Object.values you'll find that it returns an array of mixed:
static values(object: $NotNullOrVoid): Array<mixed>;
A quick google search came back with
https://davidwalsh.name/flow-object-values
So to solve your issue, you wrap Object.values(...) with any, and then inside your find arg you can type it as EtlFieldT and finally refine your type back to EtlFieldT after find.
const subject: Name = ((Object.values(etlFields): any).find(
(f: EtlFieldT) => f.purpose === 'subject',
): EtlFieldT).name;
Though you should be aware that find has the possibility of returning undefined. So to be sound, you should run the find, and declare subject if the value exists.

Related

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'TraderList'

onFetchTrader() {
this.http.get<TraderList>(this.url)
.pipe(map(resData => {
// console.log(resData);
const traderArray: any[] = [];
for (const key in resData) {
// console.log(key);
// console.log(resData[key]);
// console.log(resData.hasOwnProperty(key));
if (resData.hasOwnProperty(key)) {
// traderArray.push({clientId:key, ...resData})
traderArray.push({ clientId: key, ...resData[key] })
}
}
return traderArray;
}))
.subscribe(traders => {
// console.log(traders);
this.traders = traders;
});
I am trying to fetch the data from real time server but not able to map response data . I am getting error as Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'TraderList'.
I am trying to push the data in a array.spraed operator used ...resData[key] giving problem. Please suggest solution.

Function taking a function returning a generic type is not typesafe

I experience this when using React hooks, but it is a general TypeScript question.
You can see it in the playground
When I use the generic parameter which I finds most intuitive the function I pass in isn't completely typesafe. As long as it returns an object with the props in T the compiler is happy, but I can also add props that are not part of T. This becomes a problem when T has some optional props, because I can miss spell them and not know it.
If I instead set the return type explicit on the function I pass in, everything works as expected, but this is not an intuitive usage when the function I call have a generic parameter.
Can someone please explain why the compiler is allowing this?
const foo = <T>(f: ()=>T) => {
return f();
};
type Result = {
bar: number;
foo?:number;
}
const strange = foo<Result>(() => {
return {
bar: 42,
baz: 12, // why is this prop allowd?
FOO: 13 // ups I might think I set the foo prop, but I spelled it wrong
};
});
const expected = foo((): Result => {
return {
bar:42,
FOO: 12 // this is not allowd, which is what I wxpected
};
});
Excess property check is triggered only for "fresh" object literal.
The freshness of an object literal is lost in case of type assertion and type widening.
in strange function, there is a type widening on the return type (see issue #241). So excess properties are valid.
const strange = foo<Result>(() => {
// type widening occurs here
// freshness is lost
// No excess property check triggered
return {
bar: 42,
baz: 12, // excess property allowed
FOO: 13 // excess property allowed
};
});
in the expected function, there is no type widening since the return type is explicitly specified. the freshness of the object literal is maintained, and the excess property check is triggered
const expected = foo((): Result => {
// No type assertion
// object literal is fresh
// Excess property check is triggered
return {
bar:42,
FOO: 12 // this is not allowd, which is what I wxpected
};
});

Flow errors when dealing with nullable types

I have working on a redux reducer with the following state:
export type WishlistState = {
+deals: ?DealCollection,
+previousWishlist: ?(Deal[]),
+currentWishlist: ?(Deal[]),
+error: ?string
};
export type DealCollection = { [number]: Deal };
export const initialState: WishlistState = {
deals: null,
previousWishlist: null,
currentWishlist: null,
error: null
};
export default function wishlistReducer(
state: WishlistState = initialState,
action: WishlistAction
): WishlistState {
switch (action.type) {
case "GET_DEALS_SUCCESS":
return { ...state, deals: action.deals };
case types.GET_WISHLIST_SUCCESS:
console.log(action);
const currentWishlist: Deal[] = action.wishlistIds.map(
// ATTENTION: THIS LINE HERE
d => state.deals[d]
);
return {
...state,
currentWishlist,
previousWishlist: null,
error: null
};
// ...other cases
default:
return state;
}
}
The line I've flagged with the comment is getting a flow error on the d in the
brackets:
Cannot get `state.deals[d]` because an index signature declaring the expected key/value type is missing in null or undefined.
This is happening because of the type annotation: deals: ?DealCollection, which is made clearer if I change the line to this:
d => state.deals && state.deals[d]
Which moves the error to state.deals; and the idea is that if state.deals is null, then the callback returns null (or undefined), which is not a acceptable return type for a map callback.
I tried this and I really thought it would work:
const currentWishlist: Deal[] = !state.deals
? []
: action.wishlistIds.map(d => state.deals[d]);
It would return something acceptable if there are no deals is null, and never get to the map call. But this puts the error back on the [d] about the index signature.
Is there any way to make Flow happy in this situation?
Flow invalidates type refinements whenever a variable may have been modified. In your case, the thought of checking !state.deals is a good start; however, Flow will invalidate the fact that state.deals must have been a DealCollection because (theoretically) you could be modifying it in your map function. See https://stackoverflow.com/a/43076553/11308639 for more information on Flow type invalidation.
In your case, you can "cache" state.deals when you have refined it as a DealCollection. For example,
type Deal = string; // can be whatever
type DealCollection = { [number]: Deal };
declare var deals: ?DealCollection; // analogous to state.deals
declare var wishlistIds: number[]; // analogous to action.wishlistIds
let currentWishlist: Deal[] = [];
if (deals !== undefined && deals !== null) {
const deals_: DealCollection = deals;
currentWishlist = wishlistIds.map(d => deals_[d]);
}
Try Flow
that way you can access deals_ without Flow invalidating the refinement.

Need clarification about flowtype "exact Union Types"

I don't understand how Unions work.
Doc reference
see this doc example about exact Union Types
Problem
Code below will throw a flow error on item.rocks:
/* #flow */
type MoutainType = {|
rocks: boolean,
|};
type OceanType = {|
waves: boolean,
|};
type HolidayType = MoutainType | OceanType;
const haveHoliday = (item: HolidayType) => {
return item.rocks; //----------------> Error (but shouldn't)
}
Try yourself
See live demo
You have defined HolidayType to be an union of the types MountainType or HolidayType. Flow needs to be able to determine which type it is dealing with before it will allow you to access an exclusive member property without throwing an error.
If you test for the rocks property before attempting to access it, Flow will then be able to determine which type is in play.
const haveHoliday = (item: HolidayType) => {
if (typeof item.rocks !== 'undefined') {
// Flow now knows that this must be a MountainType
return item.rocks;
}
}
Look at the docs for Disjoint Unions and see if there is a literal value that you can use as the selector for type, and that gives you a more natural code path e.g.
type MountainType = {
terrain: 'rocks'
}
type OceanType = {
terrain: 'waves'
}
type HolidayType = MountainType | OceanType
const haveHoliday = (item: HolidayType): boolean => {
return item.terrain === 'rocks'
}

Flow - reading a union type property doesn't work when caching the common variable check

In this example, Flow shows us a way to read properties depending on the type.
I have refactored it to look like this, and it works as expected:
type Success = { success: true, value: boolean };
type Failed = { success: false, error: string };
type Response = Success | Failed;
function handleResponse(response: Response) {
const value = response.success && response.value;
const error = !response.success && response.error;
}
However, when the common property is a string, it works when doing a === check, but it doesn't if you cache the check into a variable:
type Success2 = { success: 'success', value: boolean };
type Failed2 = { success: 'not_success', error: string };
type Response2 = Success | Failed;
function handleResponse(response: Response2) {
const isSuccess = response.success === 'success';
// const value = response.success === 'success' && response.value; // WORK
const value = isSuccess && response.value; // DOESN'T WORK
}
In other words, there must be a === check (literally) before reading the variable, can't have it in a variable.
Is this a known Flow limitation, or am I missing something?
Yep, this is expected behavior. The feature you are referring to here is type refinement. The implementation is based on the standard flow-control mechanisms of Javascript, meaning that saving the result of a test and using it later will discard the type information that Flow might otherwise be able to infer. All Flow knows in your non-working example is that isSuccess is a boolean, it has no idea that true there implies that response.value exists.

Resources