TypeScript for RN Libraries
tsconfig.json
{
"compilerOptions": {
"strict": true,
"moduleResolution": "bundler",
"jsx": "react-jsx",
"lib": ["ES2022"],
"target": "ES2022",
"noEmit": true,
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"exactOptionalPropertyTypes": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true
},
"include": ["src", "example/src"],
"exclude": ["lib", "node_modules"]
}
tsconfig.build.json (used by bob):
{
"extends": "./tsconfig.json",
"compilerOptions": {
"noEmit": false,
"declaration": true,
"declarationDir": "lib/typescript"
},
"exclude": ["example", "node_modules", "lib"]
}
Exports Map (package.json)
Modern bob (v0.41+) uses ESM-only output. The commonjs target is no longer standard.
{
"main": "./lib/module/index.js",
"types": "./lib/typescript/src/index.d.ts",
"exports": {
".": {
"source": "./src/index.tsx",
"types": "./lib/typescript/src/index.d.ts",
"default": "./lib/module/index.js"
},
"./package.json": "./package.json"
}
}
Type Patterns
Extend native props
import type { ViewProps, TextProps } from 'react-native'
type MyButtonProps = ViewProps & {
label: string
variant?: 'primary' | 'secondary'
onPress?: () => void
}
Discriminated unions
type ImageSource =
| { type: 'uri'; uri: string; headers?: Record<string, string> }
| { type: 'require'; source: number }
| { type: 'base64'; data: string; mimeType: string }
Generic components
type SelectProps<T> = {
options: T[]
value: T | null
onChange: (value: T) => void
keyExtractor: (item: T) => string
labelExtractor: (item: T) => string
}
Utility types
type DeepPartial<T> = T extends object
? { [K in keyof T]?: DeepPartial<T[K]> }
: T
type RequiredKeys<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>
Rules
typenotinterface— consistent, no declaration merging surprisesimport typefor type-only imports (verbatimModuleSyntaxenforces this)- No
any— useunknown+ type guards - No
ascasts — use type guards or discriminated unions - No
!non-null assertion — use optional chaining or guards - Named exports only — no default exports
Type Guards
function isError(value: unknown): value is Error {
return value instanceof Error
}
function isImageUri(source: ImageSource): source is Extract<ImageSource, { type: 'uri' }> {
return source.type === 'uri'
}
Codegen Types
For native modules — see codegen skill for full spec patterns.
import type { TurboModule } from 'react-native'
import { TurboModuleRegistry } from 'react-native'
export interface Spec extends TurboModule {
multiply(a: number, b: number): Promise<number>
}
export default TurboModuleRegistry.getEnforcing<Spec>('RNMyLib')