Условно установить атрибуты в styleled-компоненте, используя реквизиты для индексации объекта темы с помощью TypeScript? - PullRequest
2 голосов
/ 03 марта 2020

Обзор

У меня есть проект next.js (реакция) TypeScript, который использует styled-components ThemeProvider. Я пытаюсь индексировать объект theme, который хранит размеры шрифтов rem как strings и индексируется по размерам точек (integer). Проблема, с которой я сталкиваюсь, заключается в том, что я не могу динамически установить индекс на основе пропуска, переданного из компонента. Ниже приведены сообщения об ошибках, примеры кода, предпринятые попытки и наблюдения

Сообщение об ошибке

Element implicitly has an 'any' type because expression of type 'number' can't be used to index type '{ 14: string; 12: string; 10: string; }'.

  No index signature with a parameter of type 'number' was found on type '{ 14: string; 12: string; 10: string; }'.

Примеры кода

Определение типа объекта темы

styled.d.ts

import "styled-components";

declare module "styled-components" {

  export interface DefaultTheme {
    fonts: {
      size: {
        14: string;
        12: string;
        10: string;
      };
    }
  }
}

Styled-Copmponents theme объект

theme.ts

import { DefaultTheme } from "styled-components";

const theme: DefaultTheme = {
  fonts: {
    size: {
      14: "0.875rem",
      12: "0.75rem",
      10: "0.625rem"
    },
  }
};

export { theme };

Styled-Component

component.ts

import styled from "styled-components";

const StyledThingie = styled('div')`
  /* 
     HERE IS WHERE THIS ISSUE IS 
     I want to conditionally inject the prop (if fontSize is passed) 
     into the size index to get the rem-based size, and set a default 
     font-size if no prop is passed.

     props.theme.fonts.size[10] works just fine.
  */
  font-size: ${props => (props.fontSize ? props.theme.fonts.size[props.fontSize] : props.theme.fonts.size[10])};
`;

export {
  StyledThingie
}

Реагирующий функциональный компонент

Вещи ie .tsx

import React from "react";
import styled from "styled-components";
import { StyledThingie } from "./components.ts";

const Thingie = styled(StyledThingie)`
  display: block;
`;

const ThingieComponent: React.FC<any> = (props) => {
  return(
    <Thingie fontSize={14}> // HERE IS WHERE I"M PASSING THE PROP
      <p>This paragraph should be set to 0.875rem or theme.fonts.size[14] </p>
    </Thingie>
  );
}

Попытки решения

Я пытался определить index signature в соответствии с некоторыми из документов TypeScript , но не ясно, как это реализовать. Вот что я пытался сделать:

interface FontSize {
    [index: string]: number;
}

export type StyledThingieProps = {
    fontSize?: FontSize;
}

const StyledThingie = styled('div')<StyledThingieProps>`
  size: ${props => (props.fontSize ? props.theme.fonts.size[props.fontSize] : props.theme.fonts.size[10])};
`;

Наблюдения

У меня болит мозг ...;)

1 Ответ

1 голос
/ 04 марта 2020

Эта ошибка возникает, когда вы используете переменную для индексации типа с нединамическими c свойствами. Поэтому, чтобы обойти это, вам нужно изменить объявление типа DefaultTheme.fonts.size, чтобы иметь определение для динамических c свойств. (По сути, средство проверки типов не может узнать, будет ли переменная, переданная в индекс ([]), точно соответствовать одному из объявленных ключей. Поэтому вы должны сказать: «Я в порядке с any *»). 1004 * цифра c или строковый ключ ".)

, если вы все еще хотите объявить известные ключи:

  export interface DefaultTheme {
    fonts: {
      size: {
        [key: number]: string;
        14: string;
        12: string;
        10: string;
      };
    }

Но, если вас не волнует, что указано * Размер шрифта 1026 *, вы можете просто сделать

  export interface DefaultTheme {
    fonts: {
      size: {
        [key: number]: string;
      };
    }

Эти два параметра фиксируют определение типа, чтобы принять любое число в качестве ключа. Кроме того, вы можете привести fontSize к типу ключа (используя keyof), который по сути является enum, в котором все ключи объекта являются значениями. Вы говорите контролеру типов: «эта переменная должна быть любым из указанных значений c (любой из ключей в указанном типе c)»:

// in the template where you use the font size
props.theme.fonts.size[props.fontSize as keyof typeof props.theme.fonts.size]
// or using the type declaration directly
props.theme.fonts.size[props.fontSize as keyof DefaultTheme["font"]["size"]]

Вы также были на правильном пути объявив тип размера шрифта, но вы немного ошиблись в синтаксисе:

export type StyledThingieProps = {
    fontSize?: keyof DefaultTheme["font"]["size"];
}

Это еще один способ исправить это.

...