TypeScript: react-native typings not work since typescript 3.1.1

TypeScript Version: 3.2.0-dev.20180927

Search Terms: react-native, stylesheet

Code I’m created repo to reproduce:

  1. git clone https://github.com/vovkasm/rn-ts-3.1.1.git
  2. cd rn-ts-3.1.1
  3. npm install && npm test

Paste here for easy reading

import React from 'react'
import { StyleSheet, Text } from 'react-native'

const s = StyleSheet.create({
  didNotWork: {
    fontSize: 16,
    fontWeight: '900', // if we comment this line, errors gone
    marginTop: 5, // if this line commented, errors also gone
  },
  work: {
    fontSize: 18,
//    fontWeight: '900', // when uncommented also work
  },
})

export const sample1 = <Text style={s.work} />
export const sample2 = <Text style={s.didNotWork} />
// ^ this line generate error:
// index.tsx:17:30 - error TS2322: Type 'RegisteredStyle<{ fontSize: number; fontWeight: string; marginTop: number; }>' is not assignable to type 'StyleProp<TextStyle>'.
//   Type 'RegisteredStyle<{ fontSize: number; fontWeight: string; marginTop: number; }>' is not assignable to type 'RecursiveArray<false | TextStyle | RegisteredStyle<TextStyle> | null | undefined>'.
//     Property 'length' is missing in type 'Number & { __registeredStyleBrand: { fontSize: number; fontWeight: string; marginTop: number; }; }'.
// 17 export const sample2 = <Text style={s.didNotWork} />
//                                 ~~~~~
//   node_modules/@types/react-native/index.d.ts:907:5
//     907     style?: StyleProp<TextStyle>;
//             ~~~~~
//     The expected type comes from property 'style' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<Text> & Readonly<{ children?: ReactNode; }> & Readonly<TextProps>'
export const sample3 = <Text style={{fontSize: 16, fontWeight: '900', marginTop: 5}} />

Expected behavior: No errors

Actual behavior: An error occured:

index.tsx:17:30 - error TS2322: Type 'RegisteredStyle<{ fontSize: number; fontWeight: string; marginTop: number; }>' is not assignable to type 'StyleProp<TextStyle>'.
  Type 'RegisteredStyle<{ fontSize: number; fontWeight: string; marginTop: number; }>' is not assignable to type 'RecursiveArray<false | TextStyle | RegisteredStyle<TextStyle> | null | undefined>'.
    Property 'length' is missing in type 'Number & { __registeredStyleBrand: { fontSize: number; fontWeight: string; marginTop: number; }; }'.

17 export const sample2 = <Text style={s.didNotWork} />
                                ~~~~~

  node_modules/@types/react-native/index.d.ts:907:5
    907     style?: StyleProp<TextStyle>;
            ~~~~~
    The expected type comes from property 'style' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<Text> & Readonly<{ children?: ReactNode; }> & Readonly<TextProps>'

Playground Link: https://github.com/vovkasm/rn-ts-3.1.1

Related Issues: no

Sorry, I can’t found a way to reduce this regression farther to smallest possible example. But it looks like this actually typescript regression because:

  1. It works with ts 3.0.3
  2. It magically (from my point of view) will pass test if we change code in unrelated parts (comment/uncomment some styles - see comments in index.tsx)
  3. I traced type definitions of react-native.d.ts and they appears to be correct (again, from my point of view)…

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 4
  • Comments: 15 (6 by maintainers)

Commits related to this issue

Most upvoted comments

In the same area of React Native I found a similar error, after the fix was applied and I think it can be a TypeScript error. I’ve extracted the relevant parts so the repro with the latest TSC is easy.

Code:

interface FlexStyle {
    width?: number;
}

interface ViewStyle extends FlexStyle {
    overflow?: "visible" | "hidden" | "scroll";
}

interface ImageStyle extends FlexStyle {
    overflow?: "visible" | "hidden";
}

type NamedStyles<T> = { [P in keyof T]: ViewStyle | ImageStyle };

type RegisteredStyle<T> = number & { __registeredStyleBrand: T };

type Foo = {
    bar: RegisteredStyle<ImageStyle>;
};

function create<T extends NamedStyles<T> | NamedStyles<any>>(styles: T): { [P in keyof T]: RegisteredStyle<T[P]> }
{
    return null;
}

const fooStyles: Foo = create<NamedStyles<Foo>>({
    bar: {
        width: undefined
    }
});

After calling TSC test.ts, this is the error:

test.ts:26:7 - error TS2322: Type '{ bar: RegisteredStyle<ViewStyle | ImageStyle>; }' is not assignable to type 'Foo'.
  Types of property 'bar' are incompatible.
    Type 'RegisteredStyle<ViewStyle | ImageStyle>' is not assignable to type 'RegisteredStyle<ImageStyle>'.
      Type 'RegisteredStyle<ViewStyle | ImageStyle>' is not assignable to type '{ __registeredStyleBrand: ImageStyle; }'.
        Types of property '__registeredStyleBrand' are incompatible.
          Type 'ViewStyle | ImageStyle' is not assignable to type 'ImageStyle'.
            Type 'ViewStyle' is not assignable to type 'ImageStyle'.
              Types of property 'overflow' are incompatible.
                Type '"hidden" | "visible" | "scroll"' is not assignable to type '"hidden" | "visible"'.
                  Type '"scroll"' is not assignable to type '"hidden" | "visible"'.

26 const fooStyles: Foo = create<NamedStyles<Foo>>({

overflow defined in 2 different interfaces and I think that TS compiler fails early on mismatching valuesets because the interface types are not related, but one of them could be the result of the create method call.

That is just the top of the cake that in React Native’s case, FlexStyle has the overflow property, so an the ImageStyle interface which extends FlexStyle “overwrites” the valueset, but this time I separated it for clarity.

Reduced example (same behavior in 3.0.3 and 3.2.0-dev.20180927):

interface TextStyle extends ViewStyle {
  fontWeight?: "900";
}
interface ViewStyle {
  marginTop?: number;
}

declare function create<T extends { [P in keyof T]: TextStyle | ViewStyle }>(styles: T): T;
const s = create({
  didNotWork: {
    fontWeight: '900', // if we comment this line, errors gone
    marginTop: 5, // if this line commented, errors also gone
  },
});
const f1: TextStyle = s.didNotWork;

I see the fix to react-native got stalled with the fix I suggested (because it adds a new requirement for arguments to have a string index signature). Let me suggest a different fix. Change react-native’s StyleSheet.create function in index.d.ts to have the following declaration:

export function create<T extends NamedStyles<T> | NamedStyles<any>>(styles: T): { [P in keyof T]: RegisteredStyle<T[P]> };

The NamedStyles<any> part of the union type causes the constraint to have a string index signature (such that contextual types are provided), whereas the NamedStyles<T> part of the constraint ensures that a final inferred type without a string index signature is accepted.

Best I can tell this fixes the issue and doesn’t require any other changes.

@ScreamZ I don’t know… most probably we will not be able express such types reliable until typescript 10 or 20 😕 Changes suggested by typescript team simple did not work…