TypeScript Support
Ethereum Identity Kit is built with TypeScript and provides comprehensive type definitions for all components, hooks, and utilities. The library features significant improvements to type safety and developer experience with the latest API consolidation.
Overview
The library provides:
- Complete type coverage for all exports
- Strict type checking with no
any
types - Enhanced JSDoc documentation for better IDE support
- Explicit return type interfaces for all hooks
- Type-safe component props with full IntelliSense support
- Consolidated API interfaces for better consistency
Hook Return Types
All hooks provide explicit return type interfaces for better type safety and autocompletion.
useProfileDetails
interface UseProfileDetailsReturn {
profileDetails: ProfileDetails | null
isLoading: boolean
error: Error | null
refetch: () => void
}
const result: UseProfileDetailsReturn = useProfileDetails('vitalik.eth')
useETHPrice
interface UseETHPriceReturn {
ethPrice: number | null
isLoading: boolean
error: Error | null
}
const result: UseETHPriceReturn = useETHPrice()
useFollowingState
interface UseFollowingStateReturn {
followingState: FollowingState | null
isLoading: boolean
error: Error | null
refetch: () => void
}
const result: UseFollowingStateReturn = useFollowingState({
userAddress: '0x123...',
targetAddress: '0x456...'
})
useFollowButton
The enhanced useFollowButton hook with comprehensive error handling and accessibility:
interface UseFollowButtonParams {
lookupAddress: string
connectedAddress?: string
selectedList?: number
}
interface UseFollowButtonReturn {
buttonText: string
buttonState: string
handleAction: () => Promise<void>
isLoading: boolean
isDisabled: boolean
error: string | null
clearError: () => void
ariaLabel: string
ariaPressed: boolean | undefined
pendingState: string | null
disableHover: boolean
setDisableHover: (value: boolean) => void
}
// Usage with full type safety and error handling
const buttonProps = useFollowButton({
lookupAddress: '0x123...',
connectedAddress: '0xabc...'
})
if (buttonProps.error) {
console.error('Follow action failed:', buttonProps.error)
}
Component Prop Types
ProfileCard & FullWidthProfile
The API has been consolidated with new interfaces for better type safety:
interface ProfileExtraOptions {
customFollowButton?: React.ReactNode
nameMenu?: React.ReactNode
role?: string
openListSettings?: () => void
}
interface PrefetchedData {
profile?: {
data: any
isLoading: boolean
refetch: () => void
}
stats?: {
data: any
isLoading: boolean
refetch: () => void
}
}
interface ProfileCardProps {
addressOrName: string
list?: number
connectedAddress?: string
darkMode?: boolean
showFollowerState?: boolean
showFollowButton?: boolean
showEmptySocials?: boolean
onStatClick?: (stat: string) => void
hasCommonFollowersModal?: boolean
extraOptions?: ProfileExtraOptions
prefetched?: PrefetchedData
className?: string
style?: React.CSSProperties
}
// Usage with full type safety
<ProfileCard
addressOrName="vitalik.eth"
showFollowButton={true}
extraOptions={{
customFollowButton: <CustomButton />,
role: "Developer"
}}
prefetched={{
profile: { data: profileData, isLoading: false, refetch: () => {} }
}}
onStatClick={(stat) => console.log(stat)} // stat is typed as string
/>
FollowButton
Enhanced with custom click handler support:
interface FollowButtonProps {
lookupAddress: string
connectedAddress?: string
selectedList?: number
disabled?: boolean
onDisconnectedClick?: () => void
customOnClick?: (buttonState: string) => boolean
sounds?: Record<string, string>
customClassName?: string
customLoader?: React.ReactNode
}
// Usage with custom click handler
<FollowButton
lookupAddress="0x123..."
customOnClick={(state) => {
console.log('Current state:', state)
return false // Continue with default behavior
}}
/>
FollowersYouKnow
Updated prop names for consistency:
interface FollowersYouKnowProps {
lookupAddress: string
connectedAddress?: string
showEmpty?: boolean // Previously displayEmpty
showLoading?: boolean // New prop for loading control
hasModal?: boolean
onProfileClick?: (address: string) => void
className?: string
}
// Usage with new prop names
<FollowersYouKnow
lookupAddress="vitalik.eth"
showEmpty={false}
showLoading={true}
/>
ProfileStats
Updated with new prefetched data structure:
interface ProfileStatsProps {
addressOrName: string
list?: number
containerDirection?: 'row' | 'column'
statsDirection?: 'row' | 'column'
statsStyle?: React.CSSProperties
containerStyle?: React.CSSProperties
onStatClick?: (stat: string) => void
prefetched?: {
stats: {
data: any
isLoading: boolean
}
}
}
// Usage with prefetched data
<ProfileStats
addressOrName="vitalik.eth"
prefetched={{
stats: { data: statsData, isLoading: false }
}}
/>
ProfileSocials
interface ProfileSocialsProps {
userAddress: string
name?: string
records: Record<string, string>
darkMode?: boolean
includeUrls?: boolean
iconSize?: number
isLoading?: boolean
showEmptySocials?: boolean
}
// Usage with full type safety
<ProfileSocials
userAddress="0x123..."
records={{ 'com.twitter': 'username' }}
showEmptySocials={false}
iconSize={24} // number type enforced
/>
Breaking Changes in Latest Version
The latest version introduces several API consolidation changes for better consistency:
1. Component Props Renamed
// Before
<ProfileCard options={{ followButton: <Button /> }} />
<FullWidthProfile role="Developer" options={{ followButton: <Button /> }} />
<FollowersYouKnow displayEmpty={false} />
// After
<ProfileCard extraOptions={{ customFollowButton: <Button /> }} />
<FullWidthProfile extraOptions={{ role: "Developer", customFollowButton: <Button /> }} />
<FollowersYouKnow showEmpty={false} />
2. Prefetched Data Structure
// Before - flat props
<ProfileCard
profileData={data}
prefetchedProfileLoading={loading}
refetchProfileData={refetch}
statsData={statsData}
prefetchedStatsLoading={statsLoading}
refetchStatsData={refetchStats}
/>
// After - nested structure
<ProfileCard
prefetched={{
profile: { data, isLoading: loading, refetch },
stats: { data: statsData, isLoading: statsLoading, refetch: refetchStats }
}}
/>
3. Role Prop Location
// Before - root level prop
<FullWidthProfile role="Developer" />
// After - in extraOptions
<FullWidthProfile
extraOptions={{ role: "Developer" }}
/>
4. Follow Button Prop Names
// Before - in options
<ProfileCard options={{ followButton: <CustomButton /> }} />
// After - in extraOptions with new name
<ProfileCard extraOptions={{ customFollowButton: <CustomButton /> }} />
Utility Function Types
safeLocalStorage
interface SafeLocalStorage {
getItem(key: string): string | null
setItem(key: string, value: string): void
removeItem(key: string): void
clear(): void
key(index: number): string | null
readonly length: number
}
const storage: SafeLocalStorage = safeLocalStorage
safeWindow
type SafeWindow = Window | undefined
const window: SafeWindow = safeWindow
Enhanced Error Handling Types
Action Configuration Pattern
The library now uses an ACTION_CONFIG pattern for cleaner action organization:
type ActionConfig = {
[key: string]: {
execute: () => Promise<void>
nextState: string
errorMessage: string
}
}
// Example usage in custom implementations
const ACTION_CONFIG: ActionConfig = {
follow: {
execute: async () => { /* follow logic */ },
nextState: 'Following',
errorMessage: 'Failed to follow user'
},
unfollow: {
execute: async () => { /* unfollow logic */ },
nextState: 'Follow',
errorMessage: 'Failed to unfollow user'
}
}
Advanced Type Usage
Generic Type Constraints
// Custom hook with type constraints
function useTypedProfile<T extends string | `0x${string}`>(
addressOrName: T
): UseProfileDetailsReturn {
return useProfileDetails(addressOrName)
}
// Type-safe usage
const profile1 = useTypedProfile('vitalik.eth') // ✅ Valid ENS
const profile2 = useTypedProfile('0x123...') // ✅ Valid address
Component Ref Types
import { forwardRef } from 'react'
import { ProfileCard } from 'ethereum-identity-kit'
// Forward ref with proper typing
const CustomProfileCard = forwardRef<
HTMLDivElement,
React.ComponentProps<typeof ProfileCard>
>((props, ref) => {
return <ProfileCard {...props} ref={ref} />
})
Migration Guide
From Previous Version
-
Update prop names:
// Change options → extraOptions // Change displayEmpty → showEmpty // Change followButton → customFollowButton // Move role to extraOptions
-
Restructure prefetched data:
// Convert flat props to nested prefetched object const prefetched = { profile: { data: profileData, isLoading, refetch }, stats: { data: statsData, isLoading: statsLoading, refetch: refetchStats } }
-
Update TypeScript imports if using internal types:
// Use new consolidated interfaces import type { ProfileExtraOptions, PrefetchedData } from 'ethereum-identity-kit'
Best Practices
-
Use the new nested structures for better organization:
// Good - new nested structure const extraOptions: ProfileExtraOptions = { customFollowButton: <CustomButton />, role: 'Developer' } <ProfileCard extraOptions={extraOptions} />
-
Leverage the prefetched data structure for performance:
const prefetched: PrefetchedData = { profile: { data, isLoading, refetch }, stats: { data: statsData, isLoading: statsLoading, refetch: refetchStats } } <ProfileCard prefetched={prefetched} />
-
Use discriminated unions for state management:
type LoadingState = | { status: 'loading'; data: null } | { status: 'success'; data: ProfileDetails } | { status: 'error'; error: Error }
-
Use strict null checks to catch potential runtime errors:
const profile = useProfileDetails('vitalik.eth') if (profile.profileDetails) { // TypeScript knows profileDetails is not null here console.log(profile.profileDetails.name) }
IDE Support
The library provides excellent IDE support with:
- IntelliSense autocompletion for all props and methods
- Inline documentation via JSDoc comments
- Type checking in real-time
- Go to definition for all exported types
- Refactoring support with type safety
- Migration warnings for deprecated props
Popular IDEs with full support:
- Visual Studio Code
- WebStorm
- Vim with TypeScript plugins
- Emacs with TypeScript mode