I have this interface:
interface IFormData {
[string]: string
};
export type { IFormData };
It's simple interface that accepts key-value only string. But when I use this,
const formData:IFormData = { email: '...', password: '...' };
it gives me this error:
[flow] property $key of IFormData (Indexable signature not found in object literal)
I also tried this, but it gives me same error:
var formData: IFormData; // Error
formData['email'] = ...;
formData['password'] = ...;
I searched this on google almost 2 days, but still stuck in here and I need some help!
Any advice will very appreciate it.
If you switch from interface to type, Flow seems to be a lot happier with this sort of thing:
type IFormData = {
[string]: string
}
const formData: IFormData = { email: '...', password: '...' };
I'm guessing the fact that interfaces are nominally typed, rather than structurally typed, has something to do with this.
Related
I'm new to Nuxt. I have an error retrieving data. How can I fix it?
If you are using useFetch without specifying the type of the result, typescript cannot know, what type it is.
You could tell typescript that you are receiving a list of users from an endpoint and what a user looks like by doing something like this:
interface User {
id: number
name: string
email: string
}
const {data: users} = useFetch<User[]>('api.example.com');
useFetch returns the (parsed) body of the response in data. Looking at what the URL https://reqres.in/api/users really provides, you would probably want to access the data attribute of the body. You'd need something like this to get to the list of users:
interface User {
id: number
first_name: string
last_name: string
email: string
}
const { data: body } = useFetch<{ data: User[] }>('https://reqres.in/api/users')
const users = body.value?.data ?? []
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.
My objects in Dynamodb look roughly like this:
{
userId: "GEFOeE8EsaWmq4NQ3oh7tbeVkLx1",
url: 'objectURL',
object: {}
}
I have this simple piece of code for deleting an object, when the user that owns the object requests a delete. The user argument here is a parsed JWT, by the way.
export async function deleteObject(user, url) {
let params = {
TableName: OBJECTS_TABLE,
Key: {
url: url,
},
ConditionExpression: `userId = :uid`,
ExpressionAttributeValues: {
":uid": {
S: user.sub
}
}
};
let deleteResult = await dynamoDb.delete(params).promise();
return deleteResult;
}
The problem is that it doesn't work, and I've made sure that the problem stems from the ConditionExpression by changing = to <>. I simply get this:
ConditionalCheckFailedException: The conditional request failed
I'm sure solving the problem wouldn't be difficult, but I barely have any information
Questions:
Why is the condition expression failing? Everything looks alright, and it should work. Right?
How could I debug this issue better?
The await/async is not supported by AWS SDK at the moment. Please refer this similar issue.
The SDK currently relies on CLS to trace the call context. It doesn't
work with async/await functionality right now. You can see the
discussion here.
It should work if you remove the await. Example below:-
let deleteResult = dynamodb.deleteItem(params).promise();
deleteResult.then(function (data) {
console.error("Delete item result :", JSON.stringify(data,
null, 2));
}).catch(function (err) {
console.error("Delete item result error :", JSON.stringify(err,
null, 2));
});
I figured it out. ExpressionAttributeValues can be directed used, without mentioning the datatype. The Javascript SDK does that automatically.
export async function deleteObject(user, url) {
let params = {
TableName: OBJECTS_TABLE,
Key: {
url: url,
},
ConditionExpression: `userId = :uid`,
ExpressionAttributeValues: {
":uid": user.sub
}
};
let deleteResult = await dynamoDb.delete(params).promise();
return deleteResult;
}
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.
I have a reducer for storing preferences. It has two action types. One for loading in all preferences from database and another for updating a single preference. I have a working standalone example but it breaks once used inside of my app.
The issue is that my preferences reducer only handles two types of actions, while my app has multiple reducers that fire other actions. A solution to get the code running is to add a third general type for actions not related to this reducer. That however creates Property not found in 'object type'. errors when I try to access properties of the action.
Working flow example
// #flow
const LOAD_PREFS_SUCCESS = 'LOAD_PREFS_SUCCESS';
const UPDATE_PREF = 'UPDATE_PREF';
type aType = {
+type: string
};
export type actionType = {
+type: typeof LOAD_PREFS_SUCCESS,
prefs: Array<{_id: string, value: any}>
} | {
+type: typeof UPDATE_PREF,
id: string,
value: any
};
export default (state: {} = {}, action: actionType) => {
if (action.type === LOAD_PREFS_SUCCESS) {
action.prefs.forEach(p => {
console.log(p);
});
}
switch (action.type) {
case LOAD_PREFS_SUCCESS: {
const newState = {};
action.prefs.forEach(p => {
newState[p._id] = p.value;
});
return newState;
}
case UPDATE_PREF: {
return { ...state, [action.id]: action.value };
}
default:
return state;
}
};
This is valid flow but when the app actually runs, I get an error when an action with type INIT_APP or something runs. The error says action must be one of: and then it lists the two types I have in actionType as the expected and an actual of { type: string }.
I can get the app running by adding a third type to actionType like this:
export type actionType = {
+type: typeof LOAD_PREFS_SUCCESS,
prefs: Array<{_id: string, value: any}>
} | {
+type: typeof UPDATE_PREF,
id: string,
value: any
} | {
+type: string
};
Even though the app now runs without error, it does not pass flow type check. Throwing errors of Property not found in object type. Here is an example on flow.org
Since every reducer ends up seeing every action, you'll want the type of this reducer function to include all the possible actions in your app. I usually define a single variant actionType with everything available in the app and use that in every reducer.
The reason why your last code example doesn't work is because the third, anonymous action type {type: string} is too vague. Before this, Flow could look at the two options in the action, and see that it would know which one was which based on the case statements. But with the third action type, an action like {type: "LOAD_PREFS_SUCCESS"} would match the third case in the type. So testing action.type === LOAD_PREFS_SUCCESS is no longer enough to prove that the action will have a prefs key.
So there are two ways to fix this:
If you change your action type to be more specific and include all the specific action types, your reducer should go back to type-checking.
Otherwise, add a dummy case, like | {type: "NOT-REAL"} so that Flow forces your reducer to have a default case for actions it doesn't understand.