From da047966e4c2064a48e02ff74830c99808d8194b Mon Sep 17 00:00:00 2001 From: Naman Goel Date: Tue, 14 Nov 2017 13:13:40 -0800 Subject: [PATCH] Improve types for React Native styles. Reviewed By: TheSavior Differential Revision: D6226807 fbshipit-source-id: b64a77383e6e685f4017c47fc9a5095ed63b062c --- Libraries/Inspector/ElementProperties.js | 4 +- Libraries/StyleSheet/StyleSheet.js | 24 ++- Libraries/StyleSheet/StyleSheetTypes.js | 237 ++++++++++++++++++++++- 3 files changed, 255 insertions(+), 10 deletions(-) diff --git a/Libraries/Inspector/ElementProperties.js b/Libraries/Inspector/ElementProperties.js index 1aaaf65127e630..880dcb84d173b8 100644 --- a/Libraries/Inspector/ElementProperties.js +++ b/Libraries/Inspector/ElementProperties.js @@ -25,9 +25,11 @@ const flattenStyle = require('flattenStyle'); const mapWithSeparator = require('mapWithSeparator'); const openFileInEditor = require('openFileInEditor'); +import type {StyleObj} from 'StyleSheetTypes'; + class ElementProperties extends React.Component<{ hierarchy: Array<$FlowFixMe>, - style?: Object | Array<$FlowFixMe> | number, + style?: StyleObj, source?: { fileName?: string, lineNumber?: number, diff --git a/Libraries/StyleSheet/StyleSheet.js b/Libraries/StyleSheet/StyleSheet.js index 411cad91b7c44d..ae845cab0c6ec5 100644 --- a/Libraries/StyleSheet/StyleSheet.js +++ b/Libraries/StyleSheet/StyleSheet.js @@ -18,10 +18,19 @@ const StyleSheetValidation = require('StyleSheetValidation'); const flatten = require('flattenStyle'); -export type Styles = {[key: string]: Object}; -export type StyleSheet = {[key: $Keys]: number}; -export type StyleValue = Object | number | false | null | void | ''; -export type StyleProp = StyleValue | Array; +import type { + StyleSheetStyle as _StyleSheetStyle, + Styles as _Styles, + StyleSheet as _StyleSheet, + StyleValue as _StyleValue, + StyleObj, +} from 'StyleSheetTypes'; + +export type StyleProp = StyleObj; +export type Styles = _Styles; +export type StyleSheet = _StyleSheet; +export type StyleValue = _StyleValue; +export type StyleSheetStyle = _StyleSheetStyle; let hairlineWidth = PixelRatio.roundToNearestPixel(0.4); if (hairlineWidth === 0) { @@ -29,13 +38,14 @@ if (hairlineWidth === 0) { } const absoluteFillObject = { - position: 'absolute', + position: ('absolute': 'absolute'), left: 0, right: 0, top: 0, bottom: 0, }; -const absoluteFill = ReactNativePropRegistry.register(absoluteFillObject); // This also freezes it +const absoluteFill: typeof absoluteFillObject = + ReactNativePropRegistry.register(absoluteFillObject); // This also freezes it /** * A StyleSheet is an abstraction similar to CSS StyleSheets @@ -197,7 +207,7 @@ module.exports = { * Creates a StyleSheet style reference from the given object. */ create(obj: S): StyleSheet { - const result: StyleSheet = {}; + const result = {}; for (const key in obj) { StyleSheetValidation.validateStyle(key, obj); result[key] = obj[key] && ReactNativePropRegistry.register(obj[key]); diff --git a/Libraries/StyleSheet/StyleSheetTypes.js b/Libraries/StyleSheet/StyleSheetTypes.js index 515d3e6145ea77..da2cac469fd526 100644 --- a/Libraries/StyleSheet/StyleSheetTypes.js +++ b/Libraries/StyleSheet/StyleSheetTypes.js @@ -8,8 +8,241 @@ * * @providesModule StyleSheetTypes * @flow + * @format */ + 'use strict'; -type Atom = number | bool | Object | Array; -export type StyleObj = Atom; +import AnimatedNode from 'AnimatedNode'; + +export opaque type StyleSheetStyle: number = number; + +export type ColorValue = null | string; +export type DimensionValue = null | number | string | AnimatedNode; + +export type LayoutStyle<+Dimension = DimensionValue> = { + +display?: 'none' | 'flex', + +width?: Dimension, + +height?: Dimension, + +top?: Dimension, + +bottom?: Dimension, + +left?: Dimension, + +right?: Dimension, + +minWidth?: Dimension, + +maxWidth?: Dimension, + +minHeight?: Dimension, + +maxHeight?: Dimension, + +margin?: Dimension, + +marginVertical?: Dimension, + +marginHorizontal?: Dimension, + +marginTop?: Dimension, + +marginBottom?: Dimension, + +marginLeft?: Dimension, + +marginRight?: Dimension, + +padding?: Dimension, + +paddingVertical?: Dimension, + +paddingHorizontal?: Dimension, + +paddingTop?: Dimension, + +paddingBottom?: Dimension, + +paddingLeft?: Dimension, + +paddingRight?: Dimension, + +borderWidth?: number, + +borderTopWidth?: number, + +borderBottomWidth?: number, + +borderLeftWidth?: number, + +borderRightWidth?: number, + +position?: 'absolute' | 'relative', + +flexDirection?: 'row' | 'row-reverse' | 'column' | 'column-reverse', + +flexWrap?: 'wrap' | 'nowrap', + +justifyContent?: + | 'flex-start' + | 'flex-end' + | 'center' + | 'space-between' + | 'space-around', + +alignItems?: 'flex-start' | 'flex-end' | 'center' | 'stretch' | 'baseline', + +alignSelf?: + | 'auto' + | 'flex-start' + | 'flex-end' + | 'center' + | 'stretch' + | 'baseline', + +alignContent?: + | 'flex-start' + | 'flex-end' + | 'center' + | 'stretch' + | 'space-between' + | 'space-around', + +overflow?: 'visible' | 'hidden' | 'scroll', + +flex?: number, + +flexGrow?: number, + +flexShrink?: number, + +flexBasis?: number | string, + +aspectRatio?: number, + +zIndex?: number, + +direction?: 'inherit' | 'ltr' | 'rtl', +}; + +export type TransformStyle = { + +transform?: $ReadOnlyArray< + | {+perspective: number | AnimatedNode} + | {+rotate: string} + | {+rotateX: string} + | {+rotateY: string} + | {+rotateZ: string} + | {+scale: number | AnimatedNode} + | {+scaleX: number | AnimatedNode} + | {+scaleY: number | AnimatedNode} + | {+translateX: number | AnimatedNode} + | {+translateY: number | AnimatedNode} + | { + +translate: [number | AnimatedNode, number | AnimatedNode] | AnimatedNode, + } + | {+skewX: string} + | {+skewY: string} + // TODO: what is the actual type it expects? + | {+matrix: $ReadOnlyArray | AnimatedNode}, + >, +}; + +export type ShadowStyle<+Color = ColorValue> = { + +shadowColor?: Color, + +shadowOffset?: { + +width?: number, + +height?: number, + }, + +shadowOpacity?: number | AnimatedNode, + +shadowRadius?: number, +}; + +export type ViewStyle<+Dimension = DimensionValue, +Color = ColorValue> = { + ...$Exact>, + ...$Exact>, + ...$Exact, + +backfaceVisibility?: 'visible' | 'hidden', + +backgroundColor?: Color, + +borderColor?: Color, + +borderTopColor?: Color, + +borderRightColor?: Color, + +borderBottomColor?: Color, + +borderLeftColor?: Color, + +borderRadius?: number, + +borderTopLeftRadius?: number, + +borderTopRightRadius?: number, + +borderBottomLeftRadius?: number, + +borderBottomRightRadius?: number, + +borderStyle?: 'solid' | 'dotted' | 'dashed', + +borderWidth?: number, + +borderTopWidth?: number, + +borderRightWidth?: number, + +borderBottomWidth?: number, + +borderLeftWidth?: number, + +opacity?: number | AnimatedNode, + +elevation?: number, +}; + +export type TextStyle<+Dimension = DimensionValue, +Color = ColorValue> = { + ...$Exact>, + +color?: Color, + +fontFamily?: string, + +fontSize?: number, + +fontStyle?: 'normal' | 'italic', + +fontWeight?: + | 'normal' + | 'bold' + | '100' + | '200' + | '300' + | '400' + | '500' + | '600' + | '700' + | '800' + | '900', + +fontVariant?: $ReadOnlyArray< + | 'small-caps' + | 'oldstyle-nums' + | 'lining-nums' + | 'tabular-nums' + | 'proportional-nums', + >, + +textShadowOffset?: {+width?: number, +height?: number}, + +textShadowRadius?: number, + +textShadowColor?: Color, + +letterSpacing?: number, + +lineHeight?: number, + +textAlign?: 'auto' | 'left' | 'right' | 'center' | 'justify', + +textAlignVertical?: 'auto' | 'top' | 'bottom' | 'center', + +includeFontPadding?: boolean, + +textDecorationLine?: + | 'none' + | 'underline' + | 'line-through' + | 'underline line-through', + +textDecorationStyle?: 'solid' | 'double' | 'dotted' | 'dashed', + +textDecorationColor?: Color, + +writingDirection?: 'auto' | 'ltr' | 'rtl', +}; + +export type ImageStyle<+Dimension = DimensionValue, +Color = ColorValue> = { + ...$Exact>, + +resizeMode?: 'contain' | 'cover' | 'stretch' | 'center' | 'repeat', + +tintColor?: Color, + +overlayColor?: string, +}; + +export type Style<+Dimension = DimensionValue, +Color = ColorValue> = { + ...$Exact>, + +resizeMode?: 'contain' | 'cover' | 'stretch' | 'center' | 'repeat', + +tintColor?: Color, + +overlayColor?: string, +}; + +export type StyleProp<+T> = + | null + | void + | T + | StyleSheetStyle + | number + | false + | '' + | $ReadOnlyArray>; + +// export type ViewStyleProp = StyleProp<$Shape>>; +// export type TextStyleProp = StyleProp< +// $Shape>, +// >; +// export type ImageStyleProp = StyleProp< +// $Shape>, +// >; + +export type StyleObj = StyleProp<$Shape>>; +export type StyleValue = StyleObj; + +export type ViewStyleProp = StyleObj; +export type TextStyleProp = StyleObj; +export type ImageStyleProp = StyleObj; + +export type Styles = { + +[key: string]: $Shape>, +}; +export type StyleSheet<+S: Styles> = $ObjMap StyleSheetStyle>; + +/* +Utility type get non-nullable types for specific style keys. +Useful when a component requires values for certain Style Keys. +So Instead: +``` +type Props = {position: string}; +``` +You should use: +``` +type Props = {position: TypeForStyleKey<'position'>}; +``` + +This will correctly give you the type 'absolute' | 'relative' instead of the +weak type of just string; +*/ +export type TypeForStyleKey<+key: $Keys>> = $ElementType, key>;