Aws-sdk-js: TypeScript: Many interfaces have optional members that don't match API docs

Created on 7 Jun 2017  路  12Comments  路  Source: aws/aws-sdk-js

While working with the OpsWorks client in TypeScript, I noticed that a lot of interfaces in the TypeScript definitions have all optional members, but the API docs don't clarify if anything is optional or under what circumstances they would be undefined. For example, the DescribeStacksResult interface shows its Stacks property as optional, and the Stack interface has all optional members:

https://github.com/aws/aws-sdk-js/blob/f65c11df96871889156a3b7b2fa6311675a0d962/clients/opsworks.d.ts#L1789
https://github.com/aws/aws-sdk-js/blob/f65c11df96871889156a3b7b2fa6311675a0d962/clients/opsworks.d.ts#L2789

However, the API docs do not make it clear that these properties may be undefined, except for perhaps a few cases such as VpcId which says "applicable only if the stack is running in a VPC":

http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/OpsWorks.html#describeStacks-property

Could you clarify under what circumstances various members might be undefined?

I suppose I could create various type guard functions that allow me to cast API results to my own types to verify the properties I need are present. But it seems like the discrepancy between the API docs and the type definitions should be addressed somehow. If I were writing in JavaScript, I would have no idea that I need to validate every API result to ensure a property I need didn't come back undefined.

documentation service-api typings

Most helpful comment

More generally, XXXResult and resource types tend to have most fields denoted as optional, even when this makes no sense. eg:

export interface CreateVpcResult {
  Vpc?: Vpc;
}

If the VPC wasn't created, the API will return an error. Hence, in practice it's impossible for CreateVpcResult.Vpc to be undefined, even though the type system permits that.

This makes it a pain to use result properties, as each access requires validation that the "optional" property isn't undefined:

if (vpcResult.Vpc === undefined || vpcResult.Vpc.VpcId === undefined)
  throw new Error('these can't happen, but necessary to check due to incorrect declaration as optional')

All 12 comments

Hi @sbking,

The typescript definition files (and the API documentation) are generated from service models shared among the AWS SDKs. I'll pass your feedback on to the service team to see if the documentation could be made more clear.

+1

Currently hitting issues trying to override s3.getObject. According to the type defs there are two overloads of this method.

getObject(params: S3.Types.GetObjectRequest, callback?: (err: AWSError, data: S3.Types.GetObjectOutput) => void): Request<S3.Types.GetObjectOutput, AWSError>;
getObject(callback?: (err: AWSError, data: S3.Types.GetObjectOutput) => void): Request<S3.Types.GetObjectOutput, AWSError>;

The version that only has the optional callback is not valid per the API docs.

Trying to override this like so:

  s3.getObject = (params: GetObjectRequest, callback?: (error: AWSError, output: GetObjectOutput) => void) => {
    return {} as Request<GetObjectOutput, AWSError>;
  };

gives a compiler error like this:

Error:(22, 3) TS2322:Type '(params: GetObjectRequest, callback?: (error: AWSError, output: GetObjectOutput) => void) => Requ...' is not assignable to type '{ (params: GetObjectRequest, callback?: (err: AWSError, data: GetObjectOutput) => void): Request<...'.
  Types of parameters 'params' and 'callback' are incompatible.
    Type '(err: AWSError, data: GetObjectOutput) => void' is not assignable to type 'GetObjectRequest'.
      Property 'Bucket' is missing in type '(err: AWSError, data: GetObjectOutput) => void'.

I have additional feedback for the auto-generated type definitions. It is in the DynamoDB client which basically requires for all the requests a TableName to be present in the payload. But I'm actually setting that when I create the client. So per implementation I actually don't need it, still compilation fails with a not assignable to parameter error.

Is it ok to just put that information here or should I create an additional issue to separately keep track of the feedback?

@anho Could you create a separate issue? That's something we can fix in the SDK, whereas the problem described by the OP would need to be addressed by the underlying AWS service.

@ajmath Could you create a separate issue? We can update the SDK API documentation to call out the validity of only providing a callback when parameters have been bound to a service.

More generally, XXXResult and resource types tend to have most fields denoted as optional, even when this makes no sense. eg:

export interface CreateVpcResult {
  Vpc?: Vpc;
}

If the VPC wasn't created, the API will return an error. Hence, in practice it's impossible for CreateVpcResult.Vpc to be undefined, even though the type system permits that.

This makes it a pain to use result properties, as each access requires validation that the "optional" property isn't undefined:

if (vpcResult.Vpc === undefined || vpcResult.Vpc.VpcId === undefined)
  throw new Error('these can't happen, but necessary to check due to incorrect declaration as optional')

@jeskew Any update? This is making using the AWS SDK incredibly painful

I have a similar problem - the error-first callback signatures indicate that all parameters are required, but they should all be optional, since the error is optional.

Same pain here, optional is like no types at all.

@jeskew It's a shame that that this issue seems to be ignored. It hugely devalues the type definitions if one is forced to do spurious === undefined checks or not-null assertions everywhere in order to use SDK call results. The fact that almost all SDK calls return results with many nesting levels only compounds the issue.

Perhaps the TypeScript rewrite will put more focus on this. In the meantime I use ! to deal with it.

Just wasted some time debugging some code that assumed the optional UnprocessedItems from DynamoDB DocumentClient response would be undefined. It's not just adding a !, it makes for a confusing API that basically requires you to "try it out" to see the return type before you write the code. Much like pure JS...

Was this page helpful?
0 / 5 - 0 ratings

Related issues

sebsto picture sebsto  路  3Comments

bhishp picture bhishp  路  4Comments

ranode picture ranode  路  4Comments

Usamaliaquat123 picture Usamaliaquat123  路  3Comments

fastman picture fastman  路  3Comments