Skip to main content
The AWS SDK for JavaScript v3 is written in TypeScript. Full type definitions are included in every package, giving you type hints, autocompletion, and compile-time safety — whether you write TypeScript or JavaScript.

Importing types

Use import type for type-only imports to keep your runtime bundle clean:
import type {
  GetObjectCommandInput,
  GetObjectCommandOutput,
} from "@aws-sdk/client-s3";

import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";

const client = new S3Client({ region: "us-east-1" });

const input: GetObjectCommandInput = { Bucket: "my-bucket", Key: "my-key" };
const output: GetObjectCommandOutput = await client.send(new GetObjectCommand(input));

Minimum TypeScript version

The SDK does not have an official minimum supported TypeScript version. We recommend using a recent version. You can check the TypeScript version used in SDK development in the root package.json of the repository.

Why fields are | undefined

Request and response structure fields are typed as unions with | undefined:
// Response fields may be undefined
const body = output.Body;         // SdkStream | undefined
const contentLength = output.ContentLength; // number | undefined
This is intentional. It allows AWS service APIs to evolve over time (adding or removing fields) without requiring you to update your SDK version. It also encourages defensive checking of API responses. If you have confirmed the risks and want to remove | undefined from inputs and outputs, @smithy/types provides a type transform for this:
import type { AssertiveClient } from "@smithy/types";
import { S3Client } from "@aws-sdk/client-s3";

// Fields are no longer unioned with | undefined
type AssertiveS3 = AssertiveClient<S3Client>;
See the @smithy/types documentation for full usage.

Why streaming outputs are union types

Streaming response fields (like GetObjectOutput.Body) have a union type:
// Body is a union of possible stream types
Body?: SdkStream<
  | Readable        // Node.js
  | ReadableStream  // Web Streams / browser
  | Blob
> | undefined;
This is because the requestHandler implementation varies by platform (node:https-based for Node.js, fetch-based for browsers) and service. The exact streaming type is not known at compile time.

Narrowing the streaming type

If you want to narrow the streaming payload type (for example, to always be a Node.js Readable), @smithy/types provides a type transform for this:
import type { StreamingBlobPayloadOutputTypes } from "@smithy/types";
See the @smithy/types documentation for full usage instructions.

Bare-bones vs aggregated clients

The SDK exposes two client styles. Both are fully typed.
// Import only the client shell and the commands you need.
// Enables tree-shaking — unused commands are excluded from the bundle.
import { S3Client, GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3";

const client = new S3Client({ region: "us-east-1" });
const output = await client.send(new GetObjectCommand({ Bucket: "b", Key: "k" }));
For applications where bundle size matters (browser, Lambda), prefer the bare-bones client.

Typing async credential providers

The credentials configuration field accepts either a static credentials object or an async function:
import type { AwsCredentialIdentity, Provider } from "@aws-sdk/types";
import { S3Client } from "@aws-sdk/client-s3";

// Static credentials (not recommended for production)
const staticCreds: AwsCredentialIdentity = {
  accessKeyId: "AKIAIOSFODNN7EXAMPLE",
  secretAccessKey: "...",
};

// Async credential provider
const asyncCredentials: Provider<AwsCredentialIdentity> = async () => {
  // fetch credentials from a secret store, etc.
  return {
    accessKeyId: "...",
    secretAccessKey: "...",
    expiration: new Date(Date.now() + 3600_000), // triggers refresh before expiry
  };
};

const client = new S3Client({ credentials: asyncCredentials });
If the returned credentials object includes an expiration field, the SDK will call the provider again before the credentials expire.

Build docs developers (and LLMs) love