Java 클래스에서 TypeScript 유형 정의를 생성하는 typescript-generator 사용

32771 단어 JavaTypeScripttech
백엔드가 Java로, 프런트엔드가 Type Script로 이루어지면 백엔드와 프런트엔드 간의 데이터 교환이 안전하게 이루어질 수 있습니다.
데이터 교환 방법은 API와 HTML에 JSON 데이터를 삽입하는 방법 등이 있지만 typescript-generator는 어떤 상황에서든 대상 데이터의 분류에서 TypeScript의 인터페이스를 생성할 수 있다.생성된 인터페이스를 사용하여 앞부분에서도 금형을 사용하여 안전하게 설치할 수 있다.
본고는 typescript-generator의 사용 방법과 독자적으로 확장하는 방법을 소개한다.

typerscript-generator


typescript-generator가 다음 자바의 JSON 클래스[1]가 있을 때
public class Person {
    public String name;
    public int age;
    public boolean hasChildren;
    public List<String> tags;
    public Map<String, String> emails;
}
해당 Type Script 인터페이스를 생성하는 도구입니다.
interface Person {
  name: string;
  age: number;
  hasChildren: boolean;
  tags: string[];
  emails: { [index: string]: string };
}
https://github.com/vojtechhabarta/typescript-generator
Java에서 TypeScript로의 유형 변환은 다음과 같다. Java의 원시형뿐만 아니라 Collection과 Enum 등 여러 유형에 대응한다.

Java에서 Type Script로의 유형 변환 대응 테이블, 참조https://github.com/vojtechhabarta/typescript-generator/wiki/Type-Mapping#default-type-mapping

설치 방법


Gradle을 사용할 때 build.gradle 에 다음 설정을 추가해서 가져올 수 있습니다.
apply plugin: 'cz.habarta.typescript-generator'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath group: 'cz.habarta.typescript-generator', name: 'typescript-generator-gradle-plugin', version: 'x.y.z'
    }
}

generateTypeScript {
    jsonLibrary = 'jackson2'
    outputKind = 'global'
    outputFile = 'build/sample.d.ts'
}
설정을 추가한 후 gradle generateTypeScript를 실행한 후 build/sample.d.ts에서 유형 정의를 생성합니다.메이븐을 위한 플러그인도 배포됐으며, 리덤미에서도 메이븐과Gradle Kottlin 등의 경우 설치 방법을 소개했다.

다양한 매개변수 설정하기


typescript-generator의 매개 변수는 아래 문서를 참고하여 설정합니다.매우 많은 매개 변수를 준비했기 때문에 유연한 형 전환도 이 도구의 장점이다.여기에 필요한 매개 변수의 해설과 자신이 이용하는 매개 변수를 소개한다.
http://www.habarta.cz/typescript-generator/maven/typescript-generator-maven-plugin/generate-mojo.html

필수 매개변수


유형 변환에 필요한 매개 변수는 jsonLibraryoutputKind 두 개뿐입니다.그래서 쉽게 시도할 수 있는 것도 이 도구의 장점이다.

jsonLibrary

  • JSON의 클래스를 정의할 때 사용할 라이브러리 지정
  • 지정 가능한 값은 jackson,jaxb,gson
  • outputKind

  • 출력된 Type Script 파일의 형식 지정
  • global, module 또는 ambientModule 마법사를 지정할 수 있습니다.
  • global: 전역 출력
  • module: 최고 레벨export 선언에서 출력
  • ambientModule: declare module "mod" { } 형식으로 출력
  • 기타 편리한 매개 변수


    outputFile

  • 생성된 형식 정의 파일의 경로를 설정합니다
  • outputFileType에 해당하는 확장자를 추가해야 합니다
  • .

    outputFileType

  • 내보낼 파일의 형식 지정
  • declarationFileimplementationFile 두 개 지정 가능
  • declarationFile: *.d.ts 형식으로 생성(기본값)
  • implementationFile: *.ts의 형식으로 생성
  • excludeClasses

  • 유형 정의를 생성하지 않으려는 클래스 지정
  • java.lang.Comparable 등 속성이 없는 인터페이스의 출력을 비활성화할 수 있음
  • declarePropertiesAsReadOnly

  • 속성에 대한 권한 수여readonly
  • 기본값false
  • additionalDataLibraries

  • 자바에서 자주 사용하는 라이브러리 지원opt-in
  • guava, joda, vavr 지원 추가 가능
  • mapDate

  • 자바의 Date와 관련된 형식을 어떻게 변환하는지 설정합니다
  • 지정할 수 있는 값은 asDate, asString, asNumber이고 기본값은 asDate입니다.
  • 날짜 데이터를 JSON화하면string 등으로 많이 변환
  • opt i onalProperties Declation 및 requiredAnnotations

  • optionalPropertiesDeclaration requiredAnnotations에 지정되지 않은 초대의 모든 속성을 옵션al
  • 으로 설정합니다.
  • optional의 방법으로 다음과 같은 5가지 방법을 선택할 수 있다
  • questionMark: ?를 속성에 수여
  • questionMarkAndNullableType: 속성 부여?,null과의 연합형을 모조
  • nullableType:null과의 연합형을 모조
  • nullableAndUndefinableType:null과undefined의 결합형을 모방
  • undefinableType:undefined의 연합형과 변형
  • 자바가 안전하지 않음을 감안하여 도입할 프로젝트에 따라 유연하게 설정할 수 있음
  • 독립적으로 확장을 만드는 방법


    typescript-generator는 앞서 소개한 바와 같이 다양한 매개 변수를 설정할 수 있지만 확장을 통해 더욱 유연한 변환을 할 수 있다.typescript-generator에서 제공하는 확장이 디렉토리에 있음은 다음과 같습니다.
    generateTypeScript {
        ...
        extensions = [
          'cz.habarta.typescript.generator.ext.AxiosClientExtension',
          ....
        ]
    }
    
    또한 디렉터리의 코드를 참고하여 프로젝트 특유의 변환을 실현할 수 있다.기본값을 대입하는 속성을non-nulllable로 설정하는 확장 실시 방법에 대해 간단하게 설명합니다.아래 창고에도 설치가 공개돼 있어 관심 있는 사람은 창고 코드를 참고할 수 있다.
    https://github.com/nissy-dev/typescript-generator-sample-extension

    확장 유형 만들기


    우선 계승cz.habarta.typescript.generator.Extension, 재작성getFeatures 방법과 getTransformers 방법.getFeatures 생성된 확장에 매개 변수를 제출하는 데 사용됩니다.이번에는 파라미터를 특별히 이용할 계획이 없기 때문에 EmitterExtensionFeatures의 실례를 직접 돌려드리겠습니다.getTransformers 지정된 TransformationPhase를 통해 자바빈의 TypeScript 유형 정보를 변환합니다.
    public class DefaultValueNonNullableExtension extends Extension {
    
        @Override
        public EmitterExtensionFeatures getFeatures() {
            final EmitterExtensionFeatures features = new EmitterExtensionFeatures();
            return features;
        }
    
        @Override
        public List<TransformerDefinition> getTransformers() {
            return Arrays
                    .asList(new TransformerDefinition(ModelCompiler.TransformationPhase.BeforeSymbolResolution,
                            this::transformModel));
        }
    
        protected TsModel transformModel(TsModelTransformer.Context context, TsModel model) {
            final List<TsBeanModel> beans = model.getBeans().stream()
                    .map(bean -> transformBean(context, bean))
                    .collect(Collectors.toList());
            return model.withBeans(beans);
        }
    
        protected TsBeanModel transformBean(TsModelTransformer.Context context, TsBeanModel tsBean) {
          // TODO:独自の型変換の実装のメイン
        }
    }
    
    TransformationPhase 각 Phase에서 진행되는 유형 변환을 파악하려면 아래 처리를 참고하십시오.
    https://github.com/vojtechhabarta/typescript-generator/blob/c19d407fea52612e7b7cd20620059736aec9a6a9/typescript-generator-core/src/main/java/cz/habarta/typescript/generator/compiler/ModelCompiler.java#L118-L195
    이번 경우 optionalPropertiesDeclaration에 대한 변환을 실시한 후 기본값을 대입하는 속성을non-nulllable 처리로 설정하여 지정BeforeSymbolResolution했습니다.

    확장 기본 논리 설명


    각 속성의 전환 처리는 transformBean에서 실현될 것이다.각 속성의 유형 정보는 TsBeanModelproperties에 저장되며, Java 클래스에 대한 정보context는 변수를 통해 Reflection API를 사용하여 액세스할 수 있습니다.
    다음 논리에서는 Reflection API를 사용하여 속성의 초기 값을 가져옵니다. 초기 값이null이 아니면 Union형null에서 생략됩니다.
    protected TsBeanModel transformBean(TsModelTransformer.Context context, TsBeanModel tsBean) {
        try {
            final BeanModel bean = context.getBeanModelOrigin(tsBean);
            List<TsPropertyModel> properties = tsBean.getProperties().stream().map((TsPropertyModel tsProperty) -> {
                try {
                    final Class<?> originClass = bean.getOrigin();
                    final Object instance = originClass.getConstructor().newInstance();
                    final Field field = originClass.getDeclaredField(tsProperty.getName());
                    field.setAccessible(true);
                    // プロパティの初期値を取得し、nullではないか確認する
                    if (field.get(instance) != null && tsProperty.tsType instanceof TsType.UnionType) {
                        final TsType.UnionType unionType = (TsType.UnionType) tsProperty.tsType;
                        // null を省いたユニオン型に修正する
                        return tsProperty.withTsType(unionType.remove(Arrays.asList(TsType.Null)));
                    }
    
                    return tsProperty;
                } catch (Exception e) {
                    return tsProperty;
                }
            }).collect(Collectors.toList());
            return tsBean.withProperties(properties);
        } catch (Exception e) {
            TypeScriptGenerator.getLogger()
                    .verbose(String.format("DefaultValueNonNullableExtension raised error: ", e.getMessage()));
            return tsBean;
        }
    }
    

    테스트


    마지막으로 확장이 제대로 이루어졌는지 적어 보세요.테스트에 관해서는 typescript-generator의 창고에서 이루어졌기 때문에 이러한 실시를 참고하십시오.
    class ExtensionTest {
    
        // 型変換のパラメーターを設定する
        public Settings settings() {
          final Settings settings = new Settings();
          settings.outputKind = TypeScriptOutputKind.module;
          settings.jsonLibrary = JsonLibrary.jackson2;
          settings.noFileComment = true;
          settings.noTslintDisable = true;
          settings.noEslintDisable = true;
          settings.newline = "\n";
          settings.optionalPropertiesDeclaration = OptionalPropertiesDeclaration.nullableType;
          return settings;
        }
    
        @Test
        public void test() {
            final Settings settings = settings();
            settings.extensions.add(new DefaultValueNonNullableExtension());
            final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(SampleClass.class));
            // 出力に期待されている型定義が含まれているか確認する
            assertTrue(output.contains("text0: string | null;"));
            assertTrue(output.contains("text1: string;"));
        }
    
        public static class SampleClass {
            public String text0;
            public String text1 = "hello"
        }
    }
    

    총결산


    이 글은 typescript-generator의 사용 방법에 대해 기본적인 매개 변수의 사용 방법부터 독자적으로 확장을 실시하는 방법까지 광범위하게 소개하였다.백엔드의 자바 코드를 읽으면서 TypeScript의 유형 정의를 수동으로 준비하고, TypeScript의any형을 백엔드에서 받은 값으로 개편하면 typescript-generator를 사용할 가치가 있다고 생각합니다.
    또한typescript-generator의 각 파라미터를 더 자세히 알고 싶은 사람은 공식 문서를 제외하고 아래의 글도 참고 가치가 있다.
    https://hepokon365.hatenablog.com/entry/2020/06/28/184615
    각주
    JSON이 엄중히 처리할 수 있는 반을 가리킨다.generator의 README에'Java JSON classes'라는 표현이 있기 때문에 원본의 번역을 사용했다.↩︎

    좋은 웹페이지 즐겨찾기